<!--知识库查询页面  -->
<template>
    <div class="page-wrapper">
      <!-- 引入字体组件 -->
      <font-loader />

      <!-- 顶部导航栏 -->
      <nav-bar :is-loggedin="false" :theme-icon="'fas fa-moon'" @toggle-theme="toggleTheme" @login="handleLogin" />

      <div class="home-root">
      <!-- 侧边栏组件 -->
      <chat-sidebar
        :visible.sync="showHistoryPanel"
        :conversations="conversations"
        :current-conversation-id="currentConversationId"
        app-title="新建对话"
        default-conversation-name="新建对话"
        toggle-button-title="打开侧边栏"
        :title-image="require('@/assets/img/CreditReference-logo.png')"
        @new-chat="startNewChat"
        @load-conversation="loadConversation"
        @delete-chat="deleteChat"
      />

      <!-- 主内容区 -->
      <div class="main-content" :style="mainContentStyle">
        <!-- 聊天容器 - 包含消息展示和输入框的大框体 -->
        <div class="chat-container">
          <!-- 聊天消息区域 -->
          <div class="chat-area" v-if="messages.length > 0">
          <div
            class="messages-container"
            ref="messagesContainer"
          >
            <div
              v-for="(msg, idx) in messages"
              :key="msg.messageId || `${msg.role}-${msg.created_at}-${idx}`"
              class="message-wrapper"
              :class="msg.role"
              v-show="msg.content || msg.answerContent || msg.thinkingContent || msg.role === 'user'"
            >
              <div class="message-item">
                <div class="message-avatar" v-if="msg.role === 'assistant'">
                  <img src="/img/zx.png" alt="AI助手" class="avatar-img" />
                </div>
                <div class="message-content">
                  <!-- AI 回答内容 -->
                  <!-- 🔥 分离渲染思考过程和回答内容，避免闪烁 -->
                  <div v-if="msg.role === 'assistant'" class="message-text">
                    <!-- 思考过程：一旦锁定就不再重新渲染 -->
                    <div v-if="msg.thinkingLocked && msg.thinkingContent"
                         v-html="formatThinkingBlock(msg.thinkingContent, msg.messageId)"
                         class="thinking-section"></div>

                    <!-- 回答内容：流式更新 -->
                    <div v-if="msg.answerContent"
                         v-html="formatAnswerContent(msg.answerContent)"
                         class="answer-section"></div>

                    <!-- 兼容旧格式：如果没有分离字段，使用原方法 -->
                    <div v-if="!msg.thinkingLocked && !msg.answerContent && msg.content"
                         v-html="formatMessage(msg.content)"></div>
                  </div>
                  <div class="message-actions" v-if="msg.role === 'assistant'">
                    <div class="message-time">{{ formatTime(msg.created_at) }}</div>
                    <div class="action-buttons">
                      <button
                        class="action-btn"
                        @click="copyMessage(msg.content)"
                        title="复制">
                        <i class="el-icon-document-copy"></i>
                      </button>
                      <button
                        class="action-btn"
                        @click="rethinkMessage(idx)"
                        :disabled="isLoading"
                        title="重新思考">
                        <i class="el-icon-refresh-left"></i>
                      </button>
                    </div>
                  </div>

                  <!-- 用户消息的内容和文件 -->
                  <div v-if="msg.role === 'user'" class="user-message-content">
                    <div class="message-text user-message-text">
                      <div v-if="msg.uploadedFiles && msg.uploadedFiles.length > 0" class="user-files-inline">
                        <div
                          v-for="(file, fileIdx) in msg.uploadedFiles"
                          :key="fileIdx"
                          class="user-file-inline-item"
                          @click="previewFile(file)"
                        >
                          <i class="el-icon-document"></i>
                          <span class="user-file-inline-name">{{ file.name }}</span>
                        </div>
                      </div>
                      <div class="user-message-text-body" v-html="formatMessage(msg.content)"></div>
                    </div>
                  </div>
                  <div class="message-actions" v-if="msg.role === 'user'">
                    <div class="message-time">{{ formatTime(msg.created_at) }}</div>
                    <div class="action-buttons">
                      <button
                        class="action-btn"
                        @click="copyMessage(msg.content)"
                        title="复制">
                        <i class="el-icon-document-copy"></i>
                      </button>
                      <button
                        class="action-btn"
                        @click="resendMessage(msg.content)"
                        :disabled="isLoading"
                        title="重新发送">
                        <i class="el-icon-refresh"></i>
                      </button>
                    </div>
                  </div>
                </div>
                <div class="message-avatar" v-if="msg.role === 'user'">
                  <i class="el-icon-user-solid"></i>
                </div>
              </div>
            </div>

            <!-- 加载中提示 -->
            <div v-if="isLoading" class="message-wrapper assistant">
              <div class="message-item">
                <div class="message-avatar">
                  <img src="@/assets/img/logo3.png" alt="AI助手" class="avatar-img" />
                </div>
                <div class="message-content">
                  <div class="typing-indicator">
                    <span></span>
                    <span></span>
                    <span></span>
                  </div>
                </div>
              </div>
            </div>

            <!-- 下一步问题建议 -->
            <div v-if="!isLoading && currentSuggestedQuestions.length > 0" class="suggested-questions-container">
              <div class="suggested-questions-title">
                <i class="el-icon-question"></i>
                <span>你可能还想问</span>
              </div>
              <div class="suggested-questions-list">
                <div
                  v-for="(question, idx) in currentSuggestedQuestions"
                  :key="idx"
                  class="suggested-question-chip"
                  @click="setQuickMessage(question)">
                  <span>{{ question }}</span>
                  <i class="el-icon-arrow-right"></i>
                </div>
              </div>
            </div>
          </div>
        </div>

        <!-- 欢迎界面  -->
        <div class="welcome-area" v-else>
          <div class="welcome-chat-container">
            <div class="welcome-hero">
              <!-- 顶部能力展示 Banner -->
              <div class="capability-banner">
                <div class="banner-text">
                  <div class="banner-title">征信问答助手</div>
                  <div class="banner-desc">
                    {{ appConfig.openingStatement || "专为征信从业人员打造的 AI 助手。已内嵌 100+ 部现行法规与内控制度，为您提供专业、准确的业务解答与合规指引。" }}
                  </div>
                  <!-- 开场白推荐问题（来自 Dify 配置），点击即可快速填入问题 -->
                  <div
                    v-if="openingQuestions && openingQuestions.length"
                    class="banner-opening-questions"
                  >
                    <div class="opening-questions-header">
                      <span class="opening-questions-label">这样问试试</span>
                    </div>
                    <div class="opening-questions-list">
                      <button
                        v-for="(question, idx) in openingQuestions"
                        :key="idx"
                        type="button"
                        class="opening-question-item"
                        @click="setQuickMessage(question)"
                      >
                        <span class="q-text">{{ question }}</span>
                      </button>
                    </div>
                  </div>
                </div>
                <div class="banner-img-box">
                  <img src="/img/CreditReference.gif" alt="征信知识咨询" class="banner-avatar-image" />
                </div>
              </div>

              <!-- 业务场景指引 -->
              <div class="section-label">业务场景指引</div>

              <div class="feature-dashboard">
                <!-- 主功能卡片：法规制度查询 -->
                <div
                  class="main-card"
                  @click="setQuickMessage('如何界定“恶意逾期”与“非恶意逾期”？请引用相关条文。')"
                >
                  <div class="main-card-header">
                    <div class="main-icon">
                      <i class="el-icon-notebook-2"></i>
                    </div>
                  </div>
                  <div>
                    <h3>法规制度查询</h3>
                    <p>支持《条例》、《管理办法》及内部红头文件的自然语言检索，精准定位条款。</p>
                  </div>
                  <div class="main-btn">立即提问</div>
                </div>

                <!-- 子功能网格 -->
                <div class="sub-grid">
                  <div
                    class="sub-card"
                    @click="setQuickMessage('收到用户关于个人信用报告中“职业信息”错误的异议，应如何处理？')"
                  >
                    <div class="sub-icon-box">
                      <i class="el-icon-s-operation"></i>
                    </div>
                    <div class="sub-info">
                      <h4>异议处理 SOP</h4>
                      <p>标准作业程序与话术指引</p>
                    </div>
                  </div>

                  <div
                    class="sub-card"
                    @click="setQuickMessage('帮我查询错误码 E20031 的含义及解决办法。')"
                  >
                    <div class="sub-icon-box">
                      <i class="el-icon-warning-outline"></i>
                    </div>
                    <div class="sub-info">
                      <h4>报文错误码排查</h4>
                      <p>二代征信接口规范速查</p>
                    </div>
                  </div>

                  <div
                    class="sub-card"
                    @click="setQuickMessage('请列出企业征信查询授权书的必填要素。')"
                  >
                    <div class="sub-icon-box">
                      <i class="el-icon-s-check"></i>
                    </div>
                    <div class="sub-info">
                      <h4>合规性自查</h4>
                      <p>授权书要素与留存规范</p>
                    </div>
                  </div>

                  <div
                    class="sub-card"
                    @click="setQuickMessage('帮我生成一份征信异议处理情况说明的模板。')"
                  >
                    <div class="sub-icon-box">
                      <i class="el-icon-edit-outline"></i>
                    </div>
                    <div class="sub-info">
                      <h4>文书辅助撰写</h4>
                      <p>情况说明、整改报告模板</p>
                    </div>
                  </div>
                </div>
              </div>

              <!-- 知识库覆盖范围 -->
              <div class="kb-section">
                <div class="kb-header">
                  <i class="el-icon-collection"></i>
                  <span>核心法规库已收录</span>
                </div>
                <div class="kb-tags-container">
                  <div class="kb-tag">
                    <i class="el-icon-document"></i>
                    <span>中国人民银行商务部公告2016第一号</span>
                  </div>
                  <div class="kb-tag">
                    <i class="el-icon-document"></i>
                    <span>中国人民银行关于印发《企业征信机构备案管理办法》的通知</span>
                  </div>
                  <div class="kb-tag">
                    <i class="el-icon-document"></i>
                    <span>征信业务管理办法</span>
                  </div>
                  <div class="kb-tag">
                    <i class="el-icon-document"></i>
                    <span>征信业管理条例(国务院令 第631号)</span>
                  </div>
                  <div class="kb-tag">
                    <i class="el-icon-document"></i>
                    <span>征信机构信息安全规范</span>
                  </div>
                  <div class="kb-tag">
                    <i class="el-icon-document"></i>
                    <span>征信机构管理办法</span>
                  </div>
                </div>
              </div>

              <!-- 可选：变量必填提示 -->
              <div
                class="welcome-variable-tip welcome-variable-tip-inline"
                v-if="shouldShowVariablesPanel && requiredVariables.length > 0"
              >
                <i class="el-icon-warning-outline"></i>
                <span>开始使用前，请先填写必填信息：{{ requiredVariables.map(v => v.label || v.variable).join('、') }}</span>
              </div>
            </div>
          </div>
        </div>

        <!-- 变量输入面板（隐藏独立面板，改为合并到输入框） -->
        <div class="variables-panel" v-if="false">
          <div class="panel-header" @click="variablesPanelCollapsed = !variablesPanelCollapsed">
            <div class="header-left">
              <i class="el-icon-setting"></i>
              <span class="panel-title">输入参数</span>
              <span class="variable-count">{{ appConfig.variables.length }} 个变量</span>
            </div>
            <i :class="variablesPanelCollapsed ? 'el-icon-arrow-down' : 'el-icon-arrow-up'"></i>
          </div>

          <div class="panel-content" v-show="!variablesPanelCollapsed">
            <div
              v-for="variable in appConfig.variables"
              :key="variable.variable || variable.name"
              class="variable-item">
              <div class="variable-label">
                <span>{{ variable.label || variable.variable }}</span>
                <span v-if="variable.required" class="required-mark">必填</span>
                <span v-else class="optional-mark">可选</span>
              </div>

              <!-- 文本输入 -->
              <el-input
                v-if="variable.type === 'text-input' || variable.type === 'text'"
                v-model="variableValues[variable.variable || variable.name]"
                :placeholder="'请输入' + (variable.label || variable.variable)"
                :maxlength="variable.max_length || 48"
                clearable
              />

              <!-- 段落输入 -->
              <el-input
                v-else-if="variable.type === 'paragraph'"
                type="textarea"
                v-model="variableValues[variable.variable || variable.name]"
                :placeholder="'请输入' + (variable.label || variable.variable)"
                :rows="3"
                :maxlength="variable.max_length || 1000"
                clearable
              />

              <!-- 数字输入 -->
              <el-input-number
                v-else-if="variable.type === 'number'"
                v-model="variableValues[variable.variable || variable.name]"
                :min="variable.min || 0"
                :max="variable.max || 999999"
                controls-position="right"
                style="width: 100%"
              />

              <!-- 选择器 -->
              <el-select
                v-else-if="variable.type === 'select'"
                v-model="variableValues[variable.variable || variable.name]"
                :placeholder="'请选择' + (variable.label || variable.variable)"
                style="width: 100%"
                clearable
              >
                <el-option
                  v-for="option in variable.options"
                  :key="option"
                  :label="option"
                  :value="option"
                />
              </el-select>

              <!-- 单文件上传 -->
              <div v-else-if="variable.type === 'file'" class="file-upload-area">
                <input
                  :ref="'file_' + (variable.variable || variable.name)"
                  type="file"
                  class="hidden-file"
                  @change="handleVariableFileSelect(variable, $event)"
                />

                <div v-if="!variableValues[variable.variable || variable.name]" class="upload-placeholder">
                  <el-button
                    icon="el-icon-upload"
                    size="small"
                    @click="$refs['file_' + (variable.variable || variable.name)][0].click()">
                    选择文件
                  </el-button>
                  <span class="upload-hint">支持所有格式（不支持 .doc）</span>
                </div>

                <div v-else class="file-card">
                  <i class="el-icon-document"></i>
                  <div class="file-info">
                    <div class="file-name">{{ variableValues[variable.variable || variable.name].name }}</div>
                    <div class="file-size">{{ formatSize(variableValues[variable.variable || variable.name].size) }}</div>
                  </div>
                  <el-button
                    type="danger"
                    icon="el-icon-delete"
                    size="mini"
                    circle
                    @click="removeVariableFile(variable)">
                  </el-button>
                </div>
              </div>

              <!-- 文件列表上传 -->
              <div v-else-if="variable.type === 'file-list'" class="file-list-upload-area">
                <input
                  :ref="'filelist_' + (variable.variable || variable.name)"
                  type="file"
                  multiple
                  class="hidden-file"
                  @change="handleVariableFileListSelect(variable, $event)"
                />

                <el-button
                  icon="el-icon-upload"
                  size="small"
                  @click="$refs['filelist_' + (variable.variable || variable.name)][0].click()">
                  添加文件
                </el-button>
                <span class="upload-hint">支持所有格式（不支持 .doc）</span>

                <div v-if="variableValues[variable.variable || variable.name] && variableValues[variable.variable || variable.name].length > 0" class="file-list">
                  <div
                    v-for="(file, idx) in variableValues[variable.variable || variable.name]"
                    :key="file.uid"
                    class="file-card">
                    <i class="el-icon-document"></i>
                    <div class="file-info">
                      <div class="file-name">{{ file.name }}</div>
                      <div class="file-size">{{ formatSize(file.size) }}</div>
                    </div>
                    <el-button
                      type="danger"
                      icon="el-icon-delete"
                      size="mini"
                      circle
                      @click="removeVariableFileFromList(variable, idx)">
                    </el-button>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>

        <!-- 输入区域 -->
        <div class="input-area">
          <div class="input-wrapper">
          <div class="composer" @keyup.enter="handleSend">
          <!-- 附件列表 - 有附件时显示 -->
          <div class="attachments" v-if="attachments.length">
                <div class="tip">已选择 {{ attachments.length }} 个文件</div>
            <div class="attachment-list">
              <div
                v-for="(file, idx) in attachments"
                :key="file.uid || idx"
                class="attachment-card"
              >
                <button class="att-remove" type="button" @click.stop="removeAttachment(idx)" title="移除">
                  <i class="el-icon-close"></i>
                </button>
                    <div class="file-icon">
                  <i class="el-icon-document"></i>
                </div>
                <div class="file-meta">
                  <div class="file-name">{{ file.name }}</div>
                      <div class="file-size">{{ formatSize(file.size) }}</div>
                </div>
              </div>
            </div>
          </div>
          <div class="input-row">
                <textarea
              v-model="message"
                  ref="textarea"
              class="input"
                  rows="1"
                  placeholder="请输入您的征信相关问题 (Enter 发送, Shift+Enter 换行)"
                  @keydown="handleKeydown"
                  :disabled="isLoading"
                ></textarea>
            <div class="actions">
                  <!-- 变量输入参数按钮（弹出面板） -->
                  <el-popover
                    v-if="shouldShowVariablesPanel"
                    ref="variablesPopover"
                    placement="top"
                    width="520"
                    trigger="manual"
                    v-model="variablesPopoverVisible"
                    popper-class="composer-variables-popover">
                    <div class="composer-variables">
                      <div class="variable-item" v-for="variable in appConfig.variables" :key="variable.variable || variable.name">
                        <div class="variable-label">
                          <span>{{ variable.label || variable.variable }}</span>
                          <span v-if="variable.required" class="required-mark">必填</span>
                          <span v-else class="optional-mark">可选</span>
                        </div>

                        <!-- 文本输入 -->
                        <el-input
                          v-if="variable.type === 'text-input' || variable.type === 'text'"
                          v-model="variableValues[variable.variable || variable.name]"
                          :placeholder="'请输入' + (variable.label || variable.variable)"
                          :maxlength="variable.max_length || 48"
                          clearable
                        />

                        <!-- 段落输入 -->
                        <el-input
                          v-else-if="variable.type === 'paragraph'"
                          type="textarea"
                          v-model="variableValues[variable.variable || variable.name]"
                          :placeholder="'请输入' + (variable.label || variable.variable)"
                          :rows="3"
                          :maxlength="variable.max_length || 1000"
                          clearable
                        />

                        <!-- 数字输入 -->
                        <el-input-number
                          v-else-if="variable.type === 'number'"
                          v-model="variableValues[variable.variable || variable.name]"
                          :min="variable.min || 0"
                          :max="variable.max || 999999"
                          controls-position="right"
                          style="width: 100%"
                        />

                        <!-- 选择器 -->
                        <el-select
                          v-else-if="variable.type === 'select'"
                          v-model="variableValues[variable.variable || variable.name]"
                          :placeholder="'请选择' + (variable.label || variable.variable)"
                          style="width: 100%"
                          clearable
                        >
                          <el-option
                            v-for="option in variable.options"
                            :key="option"
                            :label="option"
                            :value="option"
                          />
                        </el-select>

                        <!-- 单文件上传 -->
                        <div v-else-if="variable.type === 'file'" class="file-upload-area">
                          <input
                            :ref="'file_' + (variable.variable || variable.name)"
                            type="file"
                            class="hidden-file"
                            @change="handleVariableFileSelect(variable, $event)"
                          />

                          <div v-if="!variableValues[variable.variable || variable.name]" class="upload-placeholder">
                            <el-button
                              icon="el-icon-upload"
                              size="small"
                              @click="$refs['file_' + (variable.variable || variable.name)][0].click()">
                              选择文件
                            </el-button>
                            <span class="upload-hint">支持所有格式（不支持 .doc）</span>
                          </div>

                          <div v-else class="file-card">
                            <i class="el-icon-document"></i>
                            <div class="file-info">
                              <div class="file-name">{{ variableValues[variable.variable || variable.name].name }}</div>
                              <div class="file-size">{{ formatSize(variableValues[variable.variable || variable.name].size) }}</div>
                            </div>
                            <el-button
                              type="danger"
                              icon="el-icon-delete"
                              size="mini"
                              circle
                              @click="removeVariableFile(variable)">
                            </el-button>
                          </div>
                        </div>

                        <!-- 文件列表上传 -->
                        <div v-else-if="variable.type === 'file-list'" class="file-list-upload-area">
                          <input
                            :ref="'filelist_' + (variable.variable || variable.name)"
                            type="file"
                            multiple
                            class="hidden-file"
                            @change="handleVariableFileListSelect(variable, $event)"
                          />

                          <el-button
                            icon="el-icon-upload"
                            size="small"
                            @click="$refs['filelist_' + (variable.variable || variable.name)][0].click()">
                            添加文件
                          </el-button>
                          <span class="upload-hint">支持所有格式（不支持 .doc）</span>

                          <div v-if="variableValues[variable.variable || variable.name] && variableValues[variable.variable || variable.name].length > 0" class="file-list">
                            <div
                              v-for="(file, idx) in variableValues[variable.variable || variable.name]"
                              :key="file.uid"
                              class="file-card">
                              <i class="el-icon-document"></i>
                              <div class="file-info">
                                <div class="file-name">{{ file.name }}</div>
                                <div class="file-size">{{ formatSize(file.size) }}</div>
                              </div>
                              <el-button
                                type="danger"
                                icon="el-icon-delete"
                                size="mini"
                                circle
                                @click="removeVariableFileFromList(variable, idx)">
                              </el-button>
                            </div>
                          </div>
                        </div>
                      </div>
                    </div>
                    <div slot="reference" class="popover-center-ref"></div>
                  </el-popover>
                  <button
                    v-if="shouldShowVariablesPanel"
                    class="attach parameters-btn"
                    :class="{ alert: hasMissingRequired }"
                    type="button"
                    :disabled="isLoading"
                    @click="variablesPopoverVisible = !variablesPopoverVisible">
                    <i class="el-icon-setting"></i>
                    <sup v-if="missingRequiredCount > 0" class="badge">{{ missingRequiredCount }}</sup>
                  </button>

                  <!-- Chatflow 文件上传按钮 -->
                  <template v-if="shouldShowFileUpload">
                    <input ref="chatflowFile" class="hidden-file" type="file" multiple accept="*" @change="handleChatflowFileUpload" />
                    <button class="attach" type="button" @click="triggerChatflowFileUpload" title="上传文件（多模态对话）" :disabled="isLoading">
                      <i class="el-icon-paperclip"></i>
                    </button>
                  </template>
                  <button v-if="isLoading" class="stop" type="button" @click="stopGeneration" title="停止生成">
                    <i class="el-icon-video-pause"></i>
                  </button>
                  <button v-else class="send" type="button" @click="handleSend" title="发送" :disabled="!message.trim()">
                    <i class="el-icon-s-promotion"></i>
              </button>
            </div>
          </div>
          </div>
        </div>
        </div>
        </div>
        </div>
        </div>
  <!-- 引用与归属弹窗组件 -->
  <references-dialog
    :visible.sync="referencesDialogVisible"
    :references="referencesDialogItems"
    @close="referencesDialogVisible = false"
  />
      </div>
  </template>

  <script>
  import { mapState } from 'vuex'
  import NavBar from '@/layout/UserView/NavBar.vue'
  import FontLoader from '@/components/UserView/general/font.vue'
  import ChatSidebar from '@/components/UserView/general/sidebar.vue'
  import ReferencesDialog from '@/components/UserView/general/ReferencesDialog.vue'
  import { sendChatMessage, uploadFile, getConversations, deleteConversation, stopChatMessage, getMessages, getAppParameters } from '@/api/dify/creditReference.js'

  // 全局函数：切换分析过程显示/隐藏
  window.toggleThinkBlock = function(thinkId) {
    const content = document.getElementById(`content-${thinkId}`)
    const icon = document.getElementById(`icon-${thinkId}`)

    if (content && icon) {
      if (content.classList.contains('collapsed')) {
        // 展开
        content.classList.remove('collapsed')
        icon.classList.remove('el-icon-arrow-down')
        icon.classList.add('el-icon-arrow-up')
      } else {
        // 折叠
        content.classList.add('collapsed')
        icon.classList.remove('el-icon-arrow-up')
        icon.classList.add('el-icon-arrow-down')
      }
    }
  }

  // 全局映射与函数：打开引用和归属弹窗
  if (!window.__referencesMap) window.__referencesMap = {}
  window.openReferencesDialog = function(refId) {
    const list = window.__referencesMap[refId] || []
    const evt = new CustomEvent('open-references', { detail: { items: list } })
    window.dispatchEvent(evt)
  }

  // 打开单条引用弹窗
  window.openSingleReference = function(refId, idx) {
    const list = window.__referencesMap[refId] || []
    const item = list[idx] ? [list[idx]] : []
    const evt = new CustomEvent('open-references', { detail: { items: item } })
    window.dispatchEvent(evt)
  }

  // 打开某个文件分组的引用
  window.openReferenceGroup = function(refId, groupId) {
    const list = window.__referencesMap[refId] || []
    const groups = (window.__referencesGroupMap && window.__referencesGroupMap[refId]) || []
    const group = groups.find(g => g.id === groupId)
    const items = group ? group.indices.map(i => list[i]).filter(Boolean) : []
    const evt = new CustomEvent('open-references', { detail: { items } })
    window.dispatchEvent(evt)
  }

  export default {
    name: 'CreditReference',
    components: {
      NavBar,
      FontLoader,
      ChatSidebar,
      ReferencesDialog
    },
    data() {
      return {
        appName: 'creditReference', // App名称，用于后端查询对应的API Key
        message: '',
        messages: [],
        attachments: [],
        sidebarCollapsed: false,
        showHistoryPanel: true,
        currentConversationId: null,
        conversations: [],
        isLoading: false,
        isLoadingHistory: false,  // 新增：历史记录加载状态
        currentTaskId: null,
        abortController: null,
        streamController: null,
        // 引用与归属弹窗
        referencesDialogVisible: false,
        referencesDialogItems: [],
        // Dify 应用配置
        appConfig: {
          openingStatement: '',
          suggestedQuestions: [],
          suggestedQuestionsAfterAnswer: false,
          fileUploadEnabled: false, // Chatflow 文件上传功能是否启用（对应 sys.files）
          userInputForm: [], // 用户输入表单配置（包含所有变量定义）
          variables: [], // 所有类型的变量列表（从 userInputForm 中提取）
          mode: '' // 应用模式：agent, chat, completion, workflow 等
        },
        // 当前消息的下一步建议
        currentSuggestedQuestions: [],
        // 变量输入面板
        variablesPanelCollapsed: false, // 变量面板是否折叠
        variableValues: {}, // 当前输入的变量值 { variableName: value }
        // 当前会话已提交的变量（用于续接会话）
        conversationInputs: {}, // 存储已提交的变量，用于续接会话时重复传递
        // 变量弹出面板控制
        variablesPopoverVisible: false
      }
    },
    computed: {
      // 是否显示文件上传按钮（仅判断 Chatflow 文件上传功能）
      shouldShowFileUpload() {
        return this.appConfig.fileUploadEnabled
      },

      // 是否显示变量输入面板
      shouldShowVariablesPanel() {
        return this.appConfig.variables.length > 0
      },

      // 获取必填的变量
      requiredVariables() {
        return this.appConfig.variables.filter(v => v.required)
      },

      // 是否为 Agent 模式
      isAgentMode() {
        return this.appConfig.mode === 'agent'
      },

      // 未填必填项数量与状态（用于显著提示）
      missingRequiredCount() {
        if (!this.appConfig || !this.appConfig.variables) return 0
        const required = this.appConfig.variables.filter(v => v.required)
        let count = 0
        required.forEach(v => {
          const key = v.variable || v.name
          const val = this.variableValues[key]
          if (v.type === 'file' || v.type === 'file-list') {
            if (!val || (Array.isArray(val) && val.length === 0)) count++
          } else if (!val || val.toString().trim() === '') {
            count++
          }
        })
        return count
      },

      hasMissingRequired() {
        return this.missingRequiredCount > 0 && !this.currentConversationId
      },

      // 主内容区动态居中样式（将消息区和输入框作为整体进行居中）
      mainContentStyle() {
        // 侧边栏宽度为 300px
        const sidebarWidth = 300
        // 当侧边栏打开时，向右偏移侧边栏宽度的一半，使整个内容区在剩余空间居中
        const offset = this.showHistoryPanel ? sidebarWidth / 2 : 0
        return {
          marginLeft: `${offset}px`,
          transition: 'margin-left 0.3s ease'
        }
      },

      // 输入区域动态定位样式（因为是 fixed 定位，需要单独处理）
      inputAreaStyle() {
        const sidebarWidth = 300
        const offset = this.showHistoryPanel ? sidebarWidth / 2 : 0
        return {
          left: `${offset}px`,
          transition: 'left 0.3s ease'
        }
      },

      // 从 Vuex 获取用户ID
      ...mapState({
        userId: state => state.user.userSn
      }),
      // 开场白推荐问题（最多展示 5 条）
      openingQuestions() {
        if (!this.appConfig || !Array.isArray(this.appConfig.suggestedQuestions)) {
          return []
        }
        return this.appConfig.suggestedQuestions.slice(0, 5)
      }
    },
    async mounted() {
      // 从路由参数获取 appName（如果提供）
      if (this.$route.query.appName) {
        this.appName = this.$route.query.appName
      }

      // 如果 userSn 还未加载，先获取用户信息
      if (!this.userId) {
        await this.$store.dispatch('user/getInfo')
      }

      await this.loadAppConfig()
      await this.loadConversationsList()
      this.autoResizeTextarea()

      // 如果有会话历史，可以选择自动加载最近的一个对话（可选）
      // if (this.conversations.length > 0) {
      //   await this.loadConversation(this.conversations[0].id)
      // }

      // 添加全局点击监听，用于点击外部关闭弹窗
      document.addEventListener('click', this.handleClickOutside)
      // 监听引用与归属弹窗打开事件
      window.addEventListener('open-references', this.onOpenReferences)
    },
    beforeDestroy() {
      // 组件销毁时清理资源
      if (this.abortController) {
        this.abortController.abort()
      }
      if (this.streamController && this.streamController.abort) {
        this.streamController.abort()
      }

      // 移除全局点击监听
      document.removeEventListener('click', this.handleClickOutside)
      window.removeEventListener('open-references', this.onOpenReferences)
    },
    methods: {
      // 打开引用与归属弹窗
      onOpenReferences(evt) {
        const items = (evt && evt.detail && evt.detail.items) ? evt.detail.items : []
        this.referencesDialogItems = items
        this.referencesDialogVisible = true
      },
      /**
       * 处理点击外部关闭弹窗
       */
      handleClickOutside(event) {
        // 处理变量弹窗
        if (this.variablesPopoverVisible) {
          const popoverEl = this.$refs.variablesPopover?.$el
          const popperEl = document.querySelector('.composer-variables-popover')
          const targetBtn = event.target.closest('.parameters-btn')
          if (!targetBtn && !popoverEl?.contains(event.target) && !popperEl?.contains(event.target)) {
            this.variablesPopoverVisible = false
          }
        }

        // 历史抽屉只能通过关闭按钮关闭，不响应外部点击
      },

      async loadAppConfig() {
        try {
          console.log('🔄 正在加载应用配置... App名称:', this.appName)
          const response = await getAppParameters(this.appName)

          // 打印完整的 API 返回数据，方便调试
          console.log('📥 API 返回的完整配置:', JSON.stringify(response, null, 2))

          // 提取 data 属性
          const config = response?.data || response

          if (config) {
            this.appConfig.openingStatement = config.opening_statement || ''
            this.appConfig.suggestedQuestions = config.suggested_questions || []
            this.appConfig.suggestedQuestionsAfterAnswer = config.suggested_questions_after_answer?.enabled || false

            // 读取应用模式
            this.appConfig.mode = (config.mode || config.app_mode || '').toLowerCase()

            // 打印开场白配置
            console.log('👋 开场白配置:', {
              '开场白内容': this.appConfig.openingStatement || '未配置（将使用默认文本）',
              '是否来自API': !!config.opening_statement,
              '原始API返回': config.opening_statement
            })

            // 打印推荐问题配置
            console.log('🎯 推荐问题配置:', {
              '欢迎界面推荐问题': this.appConfig.suggestedQuestions.length > 0 ? this.appConfig.suggestedQuestions : '未配置',
              '消息后推荐问题': this.appConfig.suggestedQuestionsAfterAnswer ? '已启用' : '未启用',
              '原始配置': config.suggested_questions_after_answer
            })

            // 1. 检查 Chatflow 文件上传功能是否启用（对应 sys.files）
            // 这个功能用于多模态对话，文件通过 files 参数传递
            this.appConfig.fileUploadEnabled =
              config.file_upload?.enabled ||
              config.features?.file_upload?.enabled ||
              false

            console.log('🔍 检查文件上传配置:', {
              'config.file_upload': config.file_upload,
              'config.features': config.features,
              'fileUploadEnabled': this.appConfig.fileUploadEnabled
            })

            // 2. 获取用户输入表单配置（可能包含文件变量定义）
            // 文件变量用于工作流输入，文件通过 inputs 参数传递
            this.appConfig.userInputForm = config.user_input_form || []

            console.log('🔍 用户输入表单配置:', {
              'config.user_input_form': config.user_input_form,
              'userInputFormLength': this.appConfig.userInputForm.length,
              'userInputFormDetail': this.appConfig.userInputForm
            })

            // 3. 提取所有类型的变量列表
            // 注意：user_input_form 的结构是 [{变量名: {配置}}]
            this.appConfig.variables = []
            this.appConfig.userInputForm.forEach(inputWrapper => {
              // inputWrapper 是类似 {"variable_name": {...}} 的对象
              Object.keys(inputWrapper).forEach(varName => {
                const inputConfig = inputWrapper[varName]
                // 添加所有类型的变量
                // 确保 variable 字段存在且与 key 一致
                this.appConfig.variables.push({
                  ...inputConfig,
                  variable: inputConfig.variable || varName, // 使用配置中的 variable 字段或使用 key
                  name: varName // 保留原始 key
                })
              })
            })

            // 初始化变量默认值
            const initialValues = {}
            this.appConfig.variables.forEach(v => {
              const varName = v.variable || v.name
              // 根据类型设置默认值
              if (v.type === 'file' || v.type === 'file-list') {
                initialValues[varName] = v.type === 'file-list' ? [] : null
              } else if (v.type === 'select') {
                initialValues[varName] = v.default || (v.options && v.options[0]) || ''
              } else {
                initialValues[varName] = v.default || ''
              }
            })
            // 一次性设置所有属性，确保响应式
            this.variableValues = initialValues

            console.log('📋 变量默认值初始化:', initialValues)
            console.log('📋 变量详细信息:', this.appConfig.variables.map(v => ({
              variable: v.variable,
              name: v.name,
              label: v.label,
              type: v.type,
              required: v.required,
              defaultValue: initialValues[v.variable || v.name]
            })))

            console.log('📋 应用配置加载完成:', {
              mode: this.appConfig.mode,
              isAgentMode: this.isAgentMode,
              fileUploadEnabled: this.appConfig.fileUploadEnabled,
              variablesCount: this.appConfig.variables.length,
              variables: this.appConfig.variables.map(v => ({
                variable: v.variable,
                name: v.name,
                label: v.label,
                type: v.type,
                required: v.required
              })),
              shouldShowVariablesPanel: this.shouldShowVariablesPanel,
              shouldShowFileUpload: this.shouldShowFileUpload
            })

            // 显示配置说明
            console.log('ℹ️ 应用模式:', this.appConfig.mode || '未指定')
            if (this.appConfig.variables.length > 0) {
              const varSummary = this.appConfig.variables.map(v =>
                `${v.label || v.variable}[${v.type}]${v.required ? '(必填)' : '(可选)'}`
              ).join('、')
              console.log('ℹ️ 变量配置:', varSummary)
            }
            if (this.appConfig.fileUploadEnabled) {
              console.log('ℹ️ 文件上传配置: 启用 Chatflow 文件上传（sys.files）')
            }
            if (this.isAgentMode) {
              console.log('ℹ️ Agent 模式: 将显示快捷操作卡片（如无推荐问题）')
            }
            if (this.appConfig.suggestedQuestions.length > 0) {
              console.log('ℹ️ 欢迎界面推荐问题:', this.appConfig.suggestedQuestions.length, '个')
            }
            if (this.appConfig.suggestedQuestionsAfterAnswer) {
              console.log('ℹ️ 消息后推荐问题: 已启用，AI 回复后可能显示推荐问题')
            }
          } else {
            console.warn('⚠️ API 未返回配置数据')
          }
        } catch (error) {
          console.error('❌ 加载应用配置失败:', error)
          console.error('错误详情:', {
            message: error.message,
            response: error.response?.data,
            status: error.response?.status
          })
        }
      },

      async handleSend() {
        if (!this.message.trim() || this.isLoading) return

        // 新会话时，校验必填变量
        if (!this.currentConversationId && this.appConfig.variables.length > 0) {
          const missingRequired = this.requiredVariables.filter(v => {
            const varName = v.variable || v.name
            const value = this.variableValues[varName]
            // 检查值是否为空
            if (v.type === 'file' || v.type === 'file-list') {
              return !value || (Array.isArray(value) && value.length === 0)
            }
            return !value || value.toString().trim() === ''
          })

          if (missingRequired.length > 0) {
            const varNames = missingRequired.map(v => v.label || v.variable).join('、')
            this.$message.error(`请先填写必填变量：${varNames}`)
            // 打开变量弹出面板
            this.variablesPopoverVisible = true
            return
          }
        }

        const userMessage = this.message.trim()
        this.message = ''

        const currentTime = Date.now()

        // 清空当前的建议问题（发送新消息时）
        this.currentSuggestedQuestions = []

        // 保存上传的文件信息（用于显示和预览）
        const uploadedFilesInfo = this.attachments.length > 0
          ? this.attachments.map(f => ({
              name: f.name,
              size: f.size,
              raw: f.raw,
              url: f.raw ? URL.createObjectURL(f.raw) : null
            }))
          : []

        // 添加用户消息
        this.messages.push({
          role: 'user',
          content: userMessage,
          created_at: currentTime,
          messageId: `temp-user-${currentTime}`,
          uploadedFiles: uploadedFilesInfo // 保存文件信息用于显示
        })

        // 滚动到底部
        this.$nextTick(() => {
          this.scrollToBottom()
        })

        this.isLoading = true

        // 准备文件参数和输入参数
        const files = []  // 用于 Chatflow 文件上传（sys.files）
        const inputs = {} // 用于工作流变量

        // 处理 Chatflow 文件上传
        if (this.attachments.length > 0) {
          try {
            for (const file of this.attachments) {
              const uploadResult = await uploadFile(file.raw, this.userId, this.appName)
              const difyFileId = this.extractDifyFileId(uploadResult)
              if (!difyFileId) {
                throw new Error('无法获取文件 ID')
              }
              files.push({
                type: this.getFileType(file.name),
                transfer_method: 'local_file',
                upload_file_id: difyFileId
              })
            }
            console.log('📤 已上传 ' + this.attachments.length + ' 个文件到 sys.files')
            // 文件上传成功后清空附件列表
            this.attachments = []
          } catch (error) {
            this.$message.error('文件上传失败：' + error.message)
            this.isLoading = false
            return
          }
        }

        // 处理变量输入（包括文件变量）
        if (this.appConfig.variables.length > 0) {
          try {
            for (const variable of this.appConfig.variables) {
              const varName = variable.variable || variable.name
              const value = this.variableValues[varName]

              // 处理文件类型变量
              if (variable.type === 'file' || variable.type === 'file-list') {
                if (value) {
                  if (Array.isArray(value)) {
                    // file-list
                    const uploadedFiles = []
                    for (const file of value) {
                      const uploadResult = await uploadFile(file.raw, this.userId, this.appName)
                      const difyFileId = this.extractDifyFileId(uploadResult)
                      if (!difyFileId) {
                        throw new Error(`无法获取文件 ID: ${file.name}`)
                      }
                      uploadedFiles.push({
                        type: this.getFileType(file.name),
                        transfer_method: 'local_file',
                        upload_file_id: difyFileId
                      })
                    }
                    inputs[varName] = uploadedFiles
                  } else {
                    // single file
                    const uploadResult = await uploadFile(value.raw, this.userId, this.appName)
                    const difyFileId = this.extractDifyFileId(uploadResult)
                    if (!difyFileId) {
                      throw new Error(`无法获取文件 ID: ${value.name}`)
                    }
                    inputs[varName] = {
                      type: this.getFileType(value.name),
                      transfer_method: 'local_file',
                      upload_file_id: difyFileId
                    }
                  }
                }
              } else {
                // 处理其他类型变量
                if (value !== null && value !== undefined && value !== '') {
                  inputs[varName] = value
                }
              }
            }

            console.log('📤 变量输入:', Object.keys(inputs).join(', '))
          } catch (error) {
            this.$message.error('变量处理失败：' + error.message)
            this.isLoading = false
            return
          }
        }

        // 准备 AI 消息占位
        let aiMessageIndex = this.messages.length
        this.messages.push({
          role: 'assistant',
          content: '',
          thinkingContent: '',  // 🔥 思考过程内容（一旦完成就不再变化）
          answerContent: '',    // 🔥 回答内容（流式更新）
          thinkingLocked: false, // 🔥 思考过程是否已锁定
          created_at: currentTime + 1,
          messageId: `temp-assistant-${currentTime}`
        })

        // 创建 AbortController
        this.abortController = new AbortController()

        // 临时存储当前消息的思考内容
        let currentThoughts = []
        let currentFiles = []

        // 准备请求参数
        const requestParams = {
          query: userMessage,
          user: this.userId,
          conversation_id: this.currentConversationId,
          signal: this.abortController.signal,
          appName: this.appName
        }

        // 处理 files（Chatflow 文件上传）
        if (files.length > 0) {
          requestParams.files = files
        }

        // 处理 inputs（工作流变量）
        // 智能判断是否需要传递 inputs：
        // 1. 有必填变量 → 始终传递 inputs（即使为空，让服务器验证）
        // 2. 只有非必填变量且用户填写了 → 传递 inputs
        // 3. 只有非必填变量且用户未填写 → 不传递 inputs
        // 4. 没有定义变量 → 不传递 inputs
        const hasRequiredVariables = this.requiredVariables.length > 0
        const hasInputValues = Object.keys(inputs).length > 0
        const shouldSendInputs = this.appConfig.variables.length > 0 && (hasRequiredVariables || hasInputValues)

        if (!this.currentConversationId) {
          // 新会话
          if (shouldSendInputs) {
            requestParams.inputs = hasInputValues ? inputs : {}
            this.conversationInputs = JSON.parse(JSON.stringify(requestParams.inputs))
            console.log('📤 新会话 - 传递 inputs:', hasInputValues ? '有值' : '空对象（有必填变量）')
          } else {
            console.log('📤 新会话 - 无需传递 inputs（无必填变量且无输入值）')
          }
        } else {
          // 续接会话
          if (hasInputValues) {
            // 用户修改了变量
            requestParams.inputs = inputs
            this.conversationInputs = JSON.parse(JSON.stringify(inputs))
            console.log('📤 续接会话 - 用户修改了变量，使用新值')
          } else if (Object.keys(this.conversationInputs).length > 0) {
            // 使用之前的变量
            requestParams.inputs = this.conversationInputs
            console.log('📤 续接会话 - 使用之前的变量')
          } else if (hasRequiredVariables) {
            // 有必填变量但没有值，传递空对象
            requestParams.inputs = {}
            console.log('📤 续接会话 - 传递空 inputs（有必填变量）')
          } else {
            console.log('📤 续接会话 - 无需传递 inputs')
          }
        }

        // 打印发送的参数（调试用）
        console.log('📤 最终发送参数:', {
          query: userMessage.substring(0, 50) + '...',
          user: this.userId,
          conversation_id: this.currentConversationId,
          files: requestParams.files,
          inputs: requestParams.inputs,
          hasFiles: !!requestParams.files,
          hasInputs: !!requestParams.inputs,
          filesLength: requestParams.files?.length,
          inputsKeys: requestParams.inputs ? Object.keys(requestParams.inputs) : undefined
        })

        // 发送消息
        this.streamController = sendChatMessage(
          requestParams,
          // onMessage
          (data) => {
            // 保存 task_id
            if (data.task_id && !this.currentTaskId) {
              this.currentTaskId = data.task_id
            }

            if (!this.messages[aiMessageIndex]) return

            // 根据事件类型处理
            switch (data.event) {
              case 'agent_thought':
                // Agent 思考事件
                if (data.thought) {
                  const existingThought = currentThoughts.find(t => t.id === data.id)
                  if (!existingThought) {
                    currentThoughts.push({
                      id: data.id,
                      thought: data.thought,
                      tool: data.tool || '',
                      observation: data.observation || '',
                      position: data.position || currentThoughts.length + 1
                    })
                  } else {
                    // 更新已有的思考
                    Object.assign(existingThought, {
                      thought: data.thought,
                      tool: data.tool || existingThought.tool,
                      observation: data.observation || existingThought.observation
                    })
                  }
                }
                break

              case 'message_file':
                // 文件事件
                if (data.file) {
                  const existingFile = currentFiles.find(f => f.id === data.file.id)
                  if (!existingFile) {
                    currentFiles.push(data.file)
                  }
                }
                break

              case 'message':
              case 'agent_message':
                // 常规消息事件 - answer 是增量的
                if (data.answer) {
                  // 将新的内容追加到消息中（但要重新构建完整内容）
                  this.messages[aiMessageIndex].partialAnswer =
                    (this.messages[aiMessageIndex].partialAnswer || '') + data.answer
                }
                break
            }

            // 重新构建完整内容
            let fullContent = ''

            const answerContent = this.messages[aiMessageIndex].partialAnswer || ''
            const answerHasThinkTags = answerContent.includes('<think>')

            // 【核心逻辑优化】
            // 1. 优先使用 Dify API 返回的结构化数据（agent_thought 事件）
            // 2. 如果没有 agent_thought 事件，尝试从 answer 中智能识别思考过程
            // 3. 最后才使用开发者自定义的 <think> 标签

            if (!answerHasThinkTags && currentThoughts.length > 0) {
              // 场景1：从 Dify API 的 agent_thought 事件构建思考过程
              currentThoughts.sort((a, b) => a.position - b.position)

              currentThoughts.forEach((thought, idx) => {
                if (thought.thought) {
                  let thinkContent = `**分析过程 ${idx + 1}:**\n${thought.thought.trim()}`

                  if (thought.tool) {
                    thinkContent += `\n\n**使用工具:** ${thought.tool}`
                  }
                  if (thought.observation) {
                    thinkContent += `\n\n**执行结果:** ${thought.observation}`
                  }

                  // 统一使用 <think> 标签包裹思考内容
                  fullContent += `<think>${thinkContent}</think>`
                }
              })
              console.log(`✅ 从 agent_thought 事件构建了 ${currentThoughts.length} 个思考块`)
            } else if (!answerHasThinkTags && answerContent && this.detectThinkingContent(answerContent)) {
              // 场景2：智能检测 answer 中的思考过程
              const { thinkingPart, answerPart } = this.extractThinkingContent(answerContent)
              if (thinkingPart) {
                fullContent += `<think>${thinkingPart}</think>`
                console.log('✅ 智能识别到 answer 中的思考过程')
                // 只添加非思考部分到回答中
                if (answerPart) {
                  fullContent += answerPart
                }
              } else {
                fullContent += answerContent
              }
            } else if (answerHasThinkTags) {
              // 场景3：使用开发者自定义的 <think> 标签
              console.log('ℹ️ answer 中已包含 <think> 标签，使用开发者自定义的思考内容')
              fullContent += answerContent
            } else {
              // 场景4：普通回答，直接添加
              fullContent += answerContent
            }

            // 3. 添加文件
            if (currentFiles.length > 0) {
              fullContent += '\n\n'
              currentFiles.forEach(file => {
                if (file.type === 'image') {
                  fullContent += `\n<img src="${file.url}" alt="生成的图片" style="max-width: 100%; border-radius: 8px; margin-top: 8px;" />`
                } else {
                  // 尝试多种方式获取文件名
                  let fileName = file.name || file.filename || file.title

                  // 尝试从 belongs_to 字段获取
                  if (!fileName && file.belongs_to) {
                    fileName = file.belongs_to.name || file.belongs_to.filename
                  }

                  // 尝试从 extension_data 获取
                  if (!fileName && file.extension_data) {
                    fileName = file.extension_data.name || file.extension_data.filename
                  }

                  // 尝试从 URL 查询参数中获取
                  if (!fileName && file.url) {
                    try {
                      const urlObj = new URL(file.url)
                      // 检查查询参数
                      const nameParam = urlObj.searchParams.get('name') ||
                                       urlObj.searchParams.get('filename') ||
                                       urlObj.searchParams.get('file_name')
                      if (nameParam) {
                        fileName = decodeURIComponent(nameParam)
                      } else {
                        // 尝试从路径提取
                        const pathParts = urlObj.pathname.split('/')
                        const lastPart = pathParts[pathParts.length - 1]
                        if (lastPart && lastPart !== 'file-preview') {
                          fileName = decodeURIComponent(lastPart)
                        }
                      }
                    } catch (e) {
                      console.warn('无法从 URL 提取文件名:', e)
                    }
                  }

                  // 最后的回退选项
                  if (!fileName || fileName === 'file-preview') {
                    fileName = `${file.type || 'document'}文件`
                  }

                  fullContent += `\n<div class="file-link-wrapper"><i class="el-icon-document"></i> <a href="${file.url}" target="_blank" class="file-link">${fileName}</a></div>`
                }
              })
            }

            // 更新消息内容
            this.messages[aiMessageIndex].content = fullContent

            // 🔥 分离思考过程和回答内容，避免闪烁
            const msg = this.messages[aiMessageIndex]
            const rawAnswer = fullContent

            // 检查是否包含 </think> 结束标签
            const hasEndTag = rawAnswer.includes('</think>')

            if (hasEndTag && !msg.thinkingLocked) {
              // 第一次检测到 </think>，提取并锁定思考过程
              const parts = rawAnswer.split('</think>')
              let thinkContent = parts[0].replace(/^<think>/i, '').trim()

              if (thinkContent) {
                console.log('🔒 锁定思考过程，长度:', thinkContent.length)
                msg.thinkingContent = thinkContent
                msg.thinkingLocked = true
              }

              // 提取回答部分（</think> 后面的内容）
              const remainingAnswer = parts.slice(1).join('').trim().replace(/^\s*\n+/, '')
              msg.answerContent = remainingAnswer
            } else if (msg.thinkingLocked) {
              // 思考过程已锁定，只更新回答内容
              // 需要去掉 rawAnswer 中已经处理过的思考部分
              const cleanAnswer = rawAnswer.split('</think>').slice(1).join('').trim().replace(/^\s*\n+/, '')
              msg.answerContent = cleanAnswer
            } else {
              // 还没有遇到 </think>，暂时不处理
              // 这种情况下思考过程还在流式接收中
              msg.answerContent = rawAnswer
            }

            // 🔥 关键修复：第一次收到内容时立即关闭加载状态，避免显示两个头像
            if (fullContent && this.isLoading) {
              this.isLoading = false
            }

            this.$nextTick(() => {
              this.scrollToBottom()
            })
          },
          // onEnd
          (data) => {
            this.isLoading = false
            this.currentTaskId = null
            this.abortController = null
            this.streamController = null
            this.attachments = []

            // 重置临时变量
            currentThoughts = []
            currentFiles = []

            // 保存 conversation_id
            if (data.conversation_id && !this.currentConversationId) {
              this.currentConversationId = data.conversation_id
              this.loadConversationsList()
            }

            // 处理引用和归属（知识库引用）
            if (data.metadata?.retriever_resources && data.metadata.retriever_resources.length > 0) {
              const retrieverResources = data.metadata.retriever_resources
              console.log('🔗 处理引用和归属，共', retrieverResources.length, '条')

              // 生成唯一ID并存入全局映射，点击时以弹窗展示
              const refId = `ref-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
              if (!window.__referencesMap) window.__referencesMap = {}
              window.__referencesMap[refId] = retrieverResources
              // 按文件名/来源聚合，避免同一材料重复显示
              const refGroups = []
              const nameToId = {}
              retrieverResources.forEach((resource, idx) => {
                const name = (resource.document_name ||
                              resource.document_title ||
                              (resource.metadata && (resource.metadata.filename || resource.metadata.source || resource.metadata.title)) ||
                              resource.dataset_name ||
                              `参考${idx + 1}`)
                if (nameToId[name] === undefined) {
                  const id = refGroups.length
                  nameToId[name] = id
                  refGroups.push({ id, name, indices: [idx] })
                } else {
                  refGroups[nameToId[name]].indices.push(idx)
                }
              })
              if (!window.__referencesGroupMap) window.__referencesGroupMap = {}
              window.__referencesGroupMap[refId] = refGroups

              let referencesHtml = '\n\n<!-- REFERENCES_START --><div class="references-section">'
              referencesHtml += '<div class="references-inline-header">'
              referencesHtml += '<i class="el-icon-collection"></i><span class="references-inline-label">引用</span>'
              referencesHtml += '</div>'
              referencesHtml += '<div class="references-chip-list">'
              refGroups.forEach((group) => {
                referencesHtml += `<button class=\"reference-chip\" onclick=\"window.openReferenceGroup('${refId}', ${group.id})\">`
                referencesHtml += `<i class=\"el-icon-document\"></i>`
                referencesHtml += `<span class=\"chip-name\">${group.name}</span>`
                referencesHtml += `</button>`
              })
              referencesHtml += '</div>'
              referencesHtml += '</div><!-- REFERENCES_END -->'

              // 添加到最后一条 AI 消息：
              // 1）content 用于复制等完整内容
              // 2）answerContent 是实际渲染正文的字段，必须同步追加，否则页面看不到引用区块
              if (this.messages[aiMessageIndex]) {
                const msg = this.messages[aiMessageIndex]
                msg.content = (msg.content || '') + referencesHtml
                msg.answerContent = (msg.answerContent || '') + referencesHtml
                msg.hasReferences = true
                // 强制触发视图更新，避免引用区块不刷新
                this.$set(this.messages, aiMessageIndex, msg)
              }
            }

            // 处理下一步问题建议
            if (this.appConfig.suggestedQuestionsAfterAnswer && data.metadata?.suggested_questions) {
              this.currentSuggestedQuestions = data.metadata.suggested_questions
              console.log('✨ 收到推荐问题:', this.currentSuggestedQuestions)
            } else {
              this.currentSuggestedQuestions = []
              if (this.appConfig.suggestedQuestionsAfterAnswer && !data.metadata?.suggested_questions) {
                console.log('⚠️ 已启用消息后推荐问题功能，但本次响应未返回推荐问题')
              }
            }

            this.$nextTick(() => {
              this.scrollToBottom()
              // 自动折叠思考过程
              this.autoCollapseThinkBlocks()
            })
          },
          // onError
          (error) => {
            this.isLoading = false
            this.currentTaskId = null
            this.abortController = null
            this.streamController = null

            // 重置临时变量
            currentThoughts = []
            currentFiles = []

            // 如果是 AbortError，说明是用户主动停止，不显示错误消息
            if (error.name !== 'AbortError') {
              this.$message.error('发送失败：' + (error.message || '未知错误'))
            } else {
              console.log('✅ 用户已停止生成')
            }
          }
        )
      },

      handleKeydown(e) {
        if (e.key === 'Enter' && !e.shiftKey) {
          e.preventDefault()
          this.handleSend()
        }
      },

      async stopGeneration() {
        if (!this.isLoading) {
          this.$message.warning('没有正在进行的任务')
          return
        }

        try {
          // 1. 中断 fetch 请求
          if (this.abortController) {
            try {
              this.abortController.abort()
            } catch (e) {
              // AbortError 是正常的，忽略
            }
          }

          // 2. 取消 stream reader
          if (this.streamController && this.streamController.abort) {
            try {
              this.streamController.abort()
            } catch (e) {
              // AbortError 是正常的，忽略
            }
          }

          // 3. 调用 Dify 停止 API（如果有 task_id）
          if (this.currentTaskId) {
            try {
              await stopChatMessage(this.currentTaskId, this.userId)
            } catch (apiError) {
              // 忽略停止 API 的错误
            }
          }

          // 4. 清理状态
          this.isLoading = false
          this.currentTaskId = null
          this.abortController = null
          this.streamController = null

          this.$message.success('已停止生成')
        } catch (error) {
          // 捕获并忽略 AbortError
          if (error.name !== 'AbortError') {
            console.error('停止失败:', error)
          }

          // 无论如何都要清理状态
          this.isLoading = false
          this.currentTaskId = null
          this.abortController = null
          this.streamController = null

          this.$message.info('已终止')
        }
      },

      /**
       * 🔥 格式化思考过程块（一旦锁定就不再变化，避免闪烁）
       * @param {string} thinkingContent - 思考内容
       * @param {string} messageId - 消息ID，用于生成稳定的块ID
       */
      formatThinkingBlock(thinkingContent, messageId) {
        if (!thinkingContent) return ''

        // 使用消息ID生成稳定的思考块ID
        const thinkId = `think-${messageId}`

        // 格式化思考内容
        const formatted = this.formatTextContent(thinkingContent, 'main', null)

        return `<div class="think-block">
          <div class="think-header" onclick="toggleThinkBlock('${thinkId}')">
            <i class="el-icon-view"></i>
            <span>思考过程</span>
            <i class="el-icon-arrow-up think-toggle-icon" id="icon-${thinkId}"></i>
          </div>
          <div class="think-content" id="content-${thinkId}">${formatted}</div>
        </div>`
      },

      /**
       * 🔥 格式化回答内容（流式更新，不包含思考过程）
       * @param {string} answerContent - 回答内容
       */
      formatAnswerContent(answerContent) {
        if (!answerContent) return ''
        return this.formatTextContent(answerContent, 'main', null)
      },

      formatMessage(content) {
        if (!content) return ''

        // 先清理可能存在的不完整标签
        let cleanContent = content
          .replace(/<think>\s*$/i, '') // 移除末尾的不完整开始标签
          .replace(/^\s*<\/think>/i, '') // 移除开头的不完整结束标签

        let result = ''
        let lastIndex = 0
        let thinkBlockCount = 0

        // 使用正则匹配 <think>...</think> 标签
        const thinkRegex = /<think>([\s\S]*?)<\/think>/gi
        let match

        while ((match = thinkRegex.exec(cleanContent)) !== null) {
          thinkBlockCount++
          // 添加 think 标签之前的内容（正文）
          if (match.index > lastIndex) {
            const beforeContent = cleanContent.substring(lastIndex, match.index)
            if (beforeContent.trim()) {
              result += this.formatTextContent(beforeContent, 'main')
            }
          }

          // 添加 think 内容
          const thinkContent = match[1]
          if (thinkContent.trim()) {
            result += this.formatTextContent(thinkContent, 'think')
          }

          lastIndex = thinkRegex.lastIndex
        }


        // 添加最后剩余的内容
        if (lastIndex < cleanContent.length) {
          const remainingContent = cleanContent.substring(lastIndex)
          if (remainingContent.trim()) {
            result += this.formatTextContent(remainingContent, 'main')
          }
        }

        return result || this.formatTextContent(cleanContent, 'main')
      },

      parseMarkdownTable(tableLines) {
        if (!tableLines || tableLines.length < 2) return null

        // 解析表格行
        const parseRow = (line) => {
          return line
            .split('|')
            .map(cell => {
              // 去除前后空格和 Markdown 粗体标记 **
              let cleanCell = cell.trim()
              // 移除粗体标记 **text** -> text（处理整个单元格被包裹的情况）
              cleanCell = cleanCell.replace(/^\*\*(.*?)\*\*$/g, '$1')
              // 移除单元格内所有的粗体标记
              cleanCell = cleanCell.replace(/\*\*(.*?)\*\*/g, '$1')
              return cleanCell
            })
            .filter((cell, idx, arr) => {
              // 过滤掉首尾的空单元格（由于 | 开头/结尾产生）
              return !(idx === 0 && cell === '') && !(idx === arr.length - 1 && cell === '')
            })
        }

        const rows = tableLines.map(line => parseRow(line))

        // 检查是否有分隔符行（第二行通常是 |---|---|---| 格式）
        let hasHeaderSeparator = false
        let headerRowIndex = 0
        let dataStartIndex = 1

        if (rows.length >= 2) {
          const secondRow = rows[1]
          // 检查是否为分隔符行（包含 - 或 : 字符）
          if (secondRow.every(cell => /^:?-+:?$/.test(cell))) {
            hasHeaderSeparator = true
            dataStartIndex = 2
          }
        }

        // 如果所有行的列数不一致，可能不是有效的表格
        const columnCount = rows[0].length
        const validRows = rows.filter(row => row.length === columnCount || /^:?-+:?$/.test(row[0]))

        if (validRows.length < 2) return null

        // 构建HTML表格
        let tableHtml = '<div class="markdown-table-wrapper"><table class="markdown-table">'

        // 表头
        if (hasHeaderSeparator) {
          tableHtml += '<thead><tr>'
          rows[headerRowIndex].forEach(cell => {
            tableHtml += `<th>${cell}</th>`
          })
          tableHtml += '</tr></thead>'
        }

        // 表体
        tableHtml += '<tbody>'
        for (let i = dataStartIndex; i < rows.length; i++) {
          // 跳过分隔符行
          if (rows[i].every(cell => /^:?-+:?$/.test(cell))) continue

          // 只处理列数匹配的行
          if (rows[i].length !== columnCount) continue

          tableHtml += '<tr>'
          rows[i].forEach((cell, cellIdx) => {
            // 如果没有表头，第一行也作为数据行
            const tag = !hasHeaderSeparator && i === 0 ? 'th' : 'td'
            tableHtml += `<${tag}>${cell}</${tag}>`
          })
          tableHtml += '</tr>'
        }
        tableHtml += '</tbody></table></div>'

        return tableHtml
      },

      formatTextContent(text, type = 'main') {
        if (!text) return ''

        // 思考内容：移除重复段落，避免重复占用空间
        if (type === 'think') {
          const paragraphs = text
            .split(/\n{2,}/)
            .map(p => p.trim())
            .filter(Boolean)
          const seen = new Set()
          const deduped = []
          for (const p of paragraphs) {
            const key = p.replace(/\s+/g, ' ')
            if (!seen.has(key)) {
              seen.add(key)
              deduped.push(p)
            }
          }
          text = deduped.join('\n\n')
        }

        // 0. 移除可能残留的 think 标签
        let formatted = text
          .replace(/<\/?think>/gi, '')

        // 1. 先提取并保存 HTML 标签，避免被转义
        const htmlPlaceholders = []
        // 保存引用和归属部分（包含所有嵌套的标签）
        // 使用注释标记来精确匹配完整的引用和归属块
        formatted = formatted.replace(/<!-- REFERENCES_START -->[\s\S]*?<!-- REFERENCES_END -->/g, (match) => {
          const index = htmlPlaceholders.length
          htmlPlaceholders.push(match)
          console.log(`🔗 保护引用和归属 HTML [${index}]:`, match.substring(0, 100))
          return `__HTML_PLACEHOLDER_${index}__`
        })
        // 保存文件链接包装器（包含所有嵌套的标签）
        formatted = formatted.replace(/<div class="file-link-wrapper">[\s\S]*?<\/div>/g, (match) => {
          const index = htmlPlaceholders.length
          htmlPlaceholders.push(match)
          console.log(`📎 保护文件链接 HTML [${index}]:`, match.substring(0, 100))
          return `__HTML_PLACEHOLDER_${index}__`
        })
        // 保存图片标签
        formatted = formatted.replace(/<img[^>]*>/g, (match) => {
          const index = htmlPlaceholders.length
          htmlPlaceholders.push(match)
          return `__HTML_PLACEHOLDER_${index}__`
        })
        // 保存单独的链接标签
        formatted = formatted.replace(/<a[^>]*>.*?<\/a>/g, (match) => {
          const index = htmlPlaceholders.length
          htmlPlaceholders.push(match)
          return `__HTML_PLACEHOLDER_${index}__`
        })

        // 2. 处理代码块（在转义前处理）
        const codeBlockPlaceholders = []
        formatted = formatted.replace(/```(\w+)?\n?([\s\S]*?)```/g, (match, lang, code) => {
          const index = codeBlockPlaceholders.length
          const escapedCode = code.trim()
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
          codeBlockPlaceholders.push(`<pre class="code-block"><code>${escapedCode}</code></pre>`)
          return `__CODE_BLOCK_${index}__`
        })

        // 3. 处理行内代码（在转义前处理）
        const inlineCodePlaceholders = []
        formatted = formatted.replace(/`([^`]+)`/g, (match, code) => {
          const index = inlineCodePlaceholders.length
          const escapedCode = code
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
          inlineCodePlaceholders.push(`<code class="inline-code">${escapedCode}</code>`)
          return `__INLINE_CODE_${index}__`
        })

        // 4. 转义普通文本中的 HTML 标签
        formatted = formatted
          .replace(/&/g, '&amp;')
          .replace(/</g, '&lt;')
          .replace(/>/g, '&gt;')

        // 4.1 精简空行：去掉首尾空行并合并多余空行
        formatted = formatted
          .replace(/^\n+/, '')
          .replace(/\n+$/, '')
          .replace(/\n{2,}/g, '\n')

        // 4.2 处理 Markdown 表格（在换行替换之前处理）
        const tablePlaceholders = []
        const lines = formatted.split('\n')
        let i = 0
        while (i < lines.length) {
          const line = lines[i].trim()
          // 检测表格行（包含 | 符号）
          if (line.includes('|') && line.split('|').length >= 3) {
            const tableLines = []
            let j = i
            // 收集连续的表格行
            while (j < lines.length && lines[j].trim().includes('|')) {
              tableLines.push(lines[j].trim())
              j++
            }

            // 如果至少有2行（表头+分隔符或表头+数据行），则认为是表格
            if (tableLines.length >= 2) {
              const tableHtml = this.parseMarkdownTable(tableLines)
              if (tableHtml) {
                const index = tablePlaceholders.length
                tablePlaceholders.push(tableHtml)
                // 将表格行替换为占位符，并标记要删除的行
                lines[i] = `__TABLE_PLACEHOLDER_${index}__`
                // 删除表格的其他行（从后往前删除）
                for (let k = j - 1; k > i; k--) {
                  lines.splice(k, 1)
                }
                i++
                continue
              }
            }
          }
          i++
        }
        formatted = lines.join('\n')

        // 5. 处理 Markdown 语法
        // 处理粗体
        formatted = formatted.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')

        // 处理换行
        formatted = formatted.replace(/\n/g, '<br style="line-height: 1.6; margin: 0; padding: 0;">')

        // 处理列表
        formatted = formatted.replace(/^- (.+)(<br[^>]*>)/gm, '<li>$1</li>$2')
        formatted = formatted.replace(/(<li>.*<\/li>(<br[^>]*>)?)+/g, '<ul>$&</ul>')

        // 恢复表格占位符
        tablePlaceholders.forEach((table, index) => {
          formatted = formatted.replace(`__TABLE_PLACEHOLDER_${index}__`, table)
        })

        // 6. 恢复占位符
        // 恢复代码块
        codeBlockPlaceholders.forEach((code, index) => {
          formatted = formatted.replace(`__CODE_BLOCK_${index}__`, code)
        })

        // 恢复行内代码
        inlineCodePlaceholders.forEach((code, index) => {
          formatted = formatted.replace(`__INLINE_CODE_${index}__`, code)
        })

        // 恢复 HTML 标签
        htmlPlaceholders.forEach((html, index) => {
          if (html.includes('file-link-wrapper')) {
            console.log(`📎 恢复文件链接 HTML [${index}]:`, html.substring(0, 100))
          }
          if (html.includes('references-section')) {
            console.log(`🔗 恢复引用和归属 HTML [${index}]:`, html.substring(0, 100))
          }
          formatted = formatted.replace(`__HTML_PLACEHOLDER_${index}__`, html)
        })

        // 根据类型包装内容
        if (type === 'think') {
          // 生成唯一的 ID
          const thinkId = `think-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
          return `<div class="think-block">
            <div class="think-header" onclick="toggleThinkBlock('${thinkId}')">
              <i class="el-icon-view"></i>
              <span>思考过程</span>
              <i class="el-icon-arrow-down think-toggle-icon" id="icon-${thinkId}"></i>
            </div>
            <div class="think-content collapsed" id="content-${thinkId}">${formatted}</div>
          </div>`
        } else {
          return `<div class="main-content">${formatted}</div>`
        }
      },

      async startNewChat() {
        // 如果正在加载，先停止
        if (this.isLoading) {
          await this.stopGeneration()
        }

        this.currentConversationId = null
        this.messages = []
        this.attachments = []
        this.currentTaskId = null
        this.abortController = null
        this.streamController = null
        this.currentSuggestedQuestions = []
        this.conversationInputs = {} // 清空保存的变量

        // 重置变量值为默认值
        const resetValues = {}
        this.appConfig.variables.forEach(v => {
          const varName = v.variable || v.name
          if (v.type === 'file' || v.type === 'file-list') {
            resetValues[varName] = v.type === 'file-list' ? [] : null
          } else if (v.type === 'select') {
            resetValues[varName] = v.default || (v.options && v.options[0]) || ''
          } else {
            resetValues[varName] = v.default || ''
          }
        })
        this.variableValues = resetValues

        this.$message.success('已创建新征信咨询会话')
      },

      async loadConversation(conversationId) {
        console.log('🔄 loadConversation 被调用，conversationId:', conversationId, 'isLoadingHistory:', this.isLoadingHistory)

        // 如果正在加载历史记录，直接返回
        if (this.isLoadingHistory) {
          console.log('⚠️ 正在加载历史记录，跳过此次请求')
          return
        }

        // 如果正在生成消息，先停止
        if (this.isLoading) {
          await this.stopGeneration()
        }

        // 如果点击的是当前对话，不重复加载
        if (this.currentConversationId === conversationId && this.messages.length > 0) {
          console.log('⚠️ 已经是当前对话，跳过加载')
          return
        }

        this.isLoadingHistory = true
        this.currentConversationId = conversationId
        this.messages = []
        this.attachments = []
        this.currentSuggestedQuestions = []
        this.conversationInputs = {} // 清空保存的变量（加载历史会话时无法恢复）

        // 显示加载提示
        const loading = this.$loading({
          lock: true,
          text: '加载对话历史中...',
          spinner: 'el-icon-loading',
          background: 'rgba(0, 0, 0, 0.7)'
        })

        try {
          // 获取历史消息
          const result = await getMessages(conversationId, this.userId, 50, this.appName)

          console.log('📥 API 返回的历史消息数量:', result?.data?.length)

          if (result && result.data && result.data.length > 0) {
            // API 返回的是倒序（最新的在前），需要反转为正序（最旧的在前）
            // 按 created_at 排序，从旧到新
            const historyData = [...result.data].sort((a, b) => a.created_at - b.created_at)

            console.log('📊 开始处理 %d 条历史消息', historyData.length)

            // 将每条消息转换为用户消息和AI回复（messages 已经在上面清空过了）
            historyData.forEach((msg, index) => {
              // 1. 添加用户消息
              if (msg.query) {
                // 处理用户上传的文件（从 message_files 中提取属于用户的文件）
                let uploadedFilesInfo = []
                if (msg.message_files && msg.message_files.length > 0) {
                  console.log(`      📎 检测到 ${msg.message_files.length} 个 message_files:`, msg.message_files)

                  // 过滤用户上传的文件
                  // belongs_to 可能是 'user' 或 'assistant'，也可能不存在
                  const userFiles = msg.message_files.filter(file => {
                    // 尝试多种判断方式
                    const isUserFile =
                      file.belongs_to === 'user' ||
                      file.created_by_role === 'user' ||
                      (!file.belongs_to && !file.created_by_role) // 如果没有明确标记，也认为是用户的

                    console.log(`        文件 ${file.name || file.filename}:`, {
                      belongs_to: file.belongs_to,
                      created_by_role: file.created_by_role,
                      isUserFile: isUserFile
                    })

                    return isUserFile
                  })

                  uploadedFilesInfo = userFiles.map(file => ({
                    name: file.name || file.filename || '文件',
                    size: file.size || 0,
                    url: file.url
                  }))

                  console.log(`      ✅ 提取到 ${uploadedFilesInfo.length} 个用户文件`)
                }

                const userMsg = {
                  role: 'user',
                  content: msg.query,
                  created_at: msg.created_at * 1000,
                  messageId: `${msg.id}-user`  // 添加后缀区分用户消息
                }

                // 如果有文件信息，添加到消息对象中
                if (uploadedFilesInfo.length > 0) {
                  userMsg.uploadedFiles = uploadedFilesInfo
                  console.log(`      ✅✅✅ 用户消息包含 ${uploadedFilesInfo.length} 个文件:`, uploadedFilesInfo)
                }

                this.messages.push(userMsg)
                console.log(`  [${index + 1}] ➡️ 用户: ${msg.query.substring(0, 30)}...`, uploadedFilesInfo.length > 0 ? `(${uploadedFilesInfo.length}个文件)` : '')
                console.log(`      完整消息对象:`, JSON.stringify(userMsg, null, 2))
              }

              // 2. 构建 AI 回复内容
              let assistantContent = ''

              const answerHasThinkTags = msg.answer && msg.answer.includes('<think>')
              const hasAgentThoughts = Array.isArray(msg.agent_thoughts) && msg.agent_thoughts.length > 0

              // 【核心逻辑优化】
              // 1. 优先使用 Dify API 返回的结构化数据（agent_thoughts）
              // 2. 只在 answer 中没有 <think> 标签时，才从 agent_thoughts 构建思考块
              // 3. 不再依赖固定关键词判断，完全基于 API 返回的数据结构

              if (!answerHasThinkTags && hasAgentThoughts) {
                console.log(`      ✅ 从 agent_thoughts 构建 ${msg.agent_thoughts.length} 个思考块`)
                msg.agent_thoughts.forEach((thought, idx) => {
                  if (thought.thought) {
                    let thinkContent = `**分析过程 ${idx + 1}:**\n${thought.thought.trim()}`

                    if (thought.tool) {
                      thinkContent += `\n\n**使用工具:** ${thought.tool}`
                    }
                    if (thought.observation) {
                      thinkContent += `\n\n**执行结果:** ${thought.observation}`
                    }

                    // 统一使用 <think> 标签包裹思考内容
                    assistantContent += `<think>${thinkContent}</think>`
                  }
                })
              } else if (answerHasThinkTags) {
                console.log(`      ℹ️ answer 中已包含 <think> 标签，使用开发者自定义的思考内容`)
              }

              // 2.3 添加 answer 内容
              if (msg.answer && msg.answer.trim()) {
                // 如果没有 agent_thoughts 且没有 <think> 标签，尝试智能检测
                if (!answerHasThinkTags && !hasAgentThoughts && this.detectThinkingContent(msg.answer)) {
                  const { thinkingPart, answerPart } = this.extractThinkingContent(msg.answer)
                  if (thinkingPart) {
                    assistantContent += `<think>${thinkingPart}</think>`
                    console.log('      ✅ 智能识别到历史消息中的思考过程')
                    if (answerPart) {
                      assistantContent += answerPart
                    }
                  } else {
                    assistantContent += msg.answer
                  }
                } else {
                  assistantContent += msg.answer
                }
                console.log(`      ⬅️ AI 回答: ${msg.answer.substring(0, 30)}... (${msg.answer.length}字)`)
              } else {
                console.log(`      ⚠️ 没有 answer 内容`)
              }

              // 2.3 如果有消息文件（只处理 AI 生成的文件，用户上传的文件已在用户消息中显示）
              if (msg.message_files && msg.message_files.length > 0) {
                // 只显示 AI 生成的文件（assistant 的文件）
                const assistantFiles = msg.message_files.filter(file =>
                  file.belongs_to === 'assistant' ||
                  file.created_by_role === 'assistant'
                )

                if (assistantFiles.length > 0) {
                  assistantContent += '\n\n'
                  assistantFiles.forEach((file, fileIdx) => {
                    console.log(`      📎 AI 文件 ${fileIdx + 1}:`, JSON.stringify(file, null, 2))

                  if (file.type === 'image') {
                    assistantContent += `\n<img src="${file.url}" alt="生成的图片" style="max-width: 100%; border-radius: 8px; margin-top: 8px;" />`
                  } else {
                    // 尝试多种方式获取文件名
                    let fileName = file.name || file.filename || file.title

                    // 尝试从 belongs_to 字段获取
                    if (!fileName && file.belongs_to) {
                      fileName = file.belongs_to.name || file.belongs_to.filename
                    }

                    // 尝试从 extension_data 获取
                    if (!fileName && file.extension_data) {
                      fileName = file.extension_data.name || file.extension_data.filename
                    }

                    // 尝试从 URL 查询参数中获取（Dify 可能在 URL 中包含文件名）
                    if (!fileName && file.url) {
                      try {
                        const urlObj = new URL(file.url)
                        // 检查查询参数
                        const nameParam = urlObj.searchParams.get('name') ||
                                         urlObj.searchParams.get('filename') ||
                                         urlObj.searchParams.get('file_name')
                        if (nameParam) {
                          fileName = decodeURIComponent(nameParam)
                        } else {
                          // 尝试从路径提取
                          const pathParts = urlObj.pathname.split('/')
                          const lastPart = pathParts[pathParts.length - 1]
                          if (lastPart && lastPart !== 'file-preview') {
                            fileName = decodeURIComponent(lastPart)
                          }
                        }
                      } catch (e) {
                        console.warn('无法从 URL 提取文件名:', e)
                      }
                    }

                    // 最后的回退选项
                    if (!fileName || fileName === 'file-preview') {
                      fileName = `${file.type || 'document'}文件`
                    }

                    console.log(`      📎 最终使用的文件名: ${fileName}`)
                    assistantContent += `\n<div class="file-link-wrapper"><i class="el-icon-document"></i> <a href="${file.url}" target="_blank" class="file-link">${fileName}</a></div>`
                    }
                  })
                }
              }

              // 2.4 添加引用和归属（如果有的话）
              if (msg.retriever_resources && msg.retriever_resources.length > 0) {
                console.log(`      🔗 处理 ${msg.retriever_resources.length} 条引用和归属`)

                // 生成唯一ID并存入全局映射，点击时以弹窗展示
                const refId = `ref-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
                if (!window.__referencesMap) window.__referencesMap = {}
                window.__referencesMap[refId] = msg.retriever_resources
                // 分组聚合
                const refGroups = []
                const nameToId = {}
                msg.retriever_resources.forEach((resource, idx) => {
                  const name = (resource.document_name || resource.document_title || (resource.metadata && (resource.metadata.filename || resource.metadata.source || resource.metadata.title)) || resource.dataset_name || `参考${idx + 1}`)
                  if (nameToId[name] === undefined) {
                    const id = refGroups.length
                    nameToId[name] = id
                    refGroups.push({ id, name, indices: [idx] })
                  } else {
                    refGroups[nameToId[name]].indices.push(idx)
                  }
                })
                if (!window.__referencesGroupMap) window.__referencesGroupMap = {}
                window.__referencesGroupMap[refId] = refGroups

                let referencesHtml = '\n\n<!-- REFERENCES_START --><div class="references-section">'
                referencesHtml += '<div class="references-inline-header">'
                referencesHtml += '<i class="el-icon-collection"></i><span class="references-inline-label">引用</span>'
                referencesHtml += '</div>'
                referencesHtml += '<div class="references-chip-list">'
                refGroups.forEach((group) => {
                  referencesHtml += `<button class=\"reference-chip\" onclick=\"window.openReferenceGroup('${refId}', ${group.id})\">`
                  referencesHtml += `<i class=\"el-icon-document\"></i>`
                  referencesHtml += `<span class=\"chip-name\">${group.name}</span>`
                  referencesHtml += `</button>`
                })
                referencesHtml += '</div>'
                referencesHtml += '</div><!-- REFERENCES_END -->'
                assistantContent += referencesHtml
                console.log(`      ✅ 已添加引用和归属 HTML`)
              } else {
                console.log(`      ℹ️ 该消息没有引用和归属`)
              }

              // 3. 添加 AI 回复消息（只有当有内容时才添加）
              if (assistantContent.trim()) {
                // 🔥 分离思考过程和回答内容（用于历史消息）
                let thinkingContent = ''
                let answerContent = ''
                let thinkingLocked = false

                const hasEndTag = assistantContent.includes('</think>')
                if (hasEndTag) {
                  const parts = assistantContent.split('</think>')
                  thinkingContent = parts[0].replace(/^<think>/i, '').trim()
                  answerContent = parts.slice(1).join('').trim().replace(/^\s*\n+/, '')
                  thinkingLocked = true
                } else {
                  // 没有思考过程，全是回答
                  answerContent = assistantContent
                }

                this.messages.push({
                  role: 'assistant',
                  content: assistantContent,
                  thinkingContent: thinkingContent,
                  answerContent: answerContent,
                  thinkingLocked: thinkingLocked,
                  created_at: msg.created_at * 1000 + 1,  // 稍微偏移时间戳，避免与用户消息完全相同
                  messageId: `${msg.id}-assistant`,  // 添加后缀区分 AI 消息
                  hasAgentThoughts: Boolean(msg.agent_thoughts),
                  hasFiles: msg.message_files && msg.message_files.length > 0,
                  hasReferences: msg.retriever_resources && msg.retriever_resources.length > 0
                })
              }
            })

            console.log('✅ 历史记录加载完成，共 %d 条消息（%d 轮对话）', this.messages.length, historyData.length)

            // 滚动到底部并自动折叠思考过程
            this.$nextTick(() => {
              this.scrollToBottom()
              this.autoCollapseThinkBlocks()
            })

            this.$message.success(`已加载 ${historyData.length} 轮对话`)
          } else {
            this.$message.info('该对话暂无历史记录')
          }
        } catch (error) {
          console.error('加载对话历史失败:', error)
          this.$message.error('加载对话历史失败：' + (error.message || '未知错误'))
        } finally {
          loading.close()
          this.isLoadingHistory = false  // 重置加载状态
        }
      },

      async loadConversationsList() {
        try {
          const result = await getConversations(this.userId, 50, this.appName)
          this.conversations = result.data || []
        } catch (error) {
          console.error('加载会话列表失败:', error)
        }
      },

      async deleteChat(conversationId) {
        try {
          await this.$confirm('确定要删除这条对话记录吗？', '提示', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          })

          await deleteConversation(conversationId, this.userId)

          if (this.currentConversationId === conversationId) {
            this.startNewChat()
          }

          await this.loadConversationsList()
          this.$message.success('已删除')
        } catch (error) {
          if (error !== 'cancel') {
            this.$message.error('删除失败')
          }
        }
      },

      // Chatflow 文件上传
      triggerChatflowFileUpload() {
        if (this.$refs.chatflowFile) {
          this.$refs.chatflowFile.click()
        }
      },

      handleChatflowFileUpload(e) {
        const files = Array.from((e && e.target && e.target.files) || [])
        if (!files.length) return

        const mapped = files.map(f => ({
          uid: `${Date.now()}_${Math.random().toString(36).slice(2)}`,
          name: f.name,
          size: f.size,
          raw: f
        }))

        this.attachments = this.attachments.concat(mapped)
        this.$message.success(`已选择 ${files.length} 个文件`)

        if (this.$refs.chatflowFile) {
          this.$refs.chatflowFile.value = ''
        }
      },

      removeAttachment(index) {
        if (index < 0 || index >= this.attachments.length) return
        const removed = this.attachments.splice(index, 1)[0]
        if (removed) {
          this.$message.success('已移除：' + (removed.name || '文件'))
        }
      },

      getFileType(filename) {
        const ext = (filename.split('.').pop() || '').toLowerCase()
        const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg']
        const docExts = ['txt', 'md', 'pdf', 'doc', 'docx', 'html', 'xlsx', 'xls', 'csv']

        if (imageExts.includes(ext)) return 'image'
        if (docExts.includes(ext)) return 'document'
        return 'custom'
      },

      formatSize(bytes) {
        if (bytes < 1024) return bytes + 'B'
        const kb = bytes / 1024
        if (kb < 1024) return kb.toFixed(2) + 'KB'
        const mb = kb / 1024
        return mb.toFixed(2) + 'MB'
      },

      formatTime(timestamp) {
        const date = new Date(timestamp)
        const now = new Date()
        const diff = now - date

        if (diff < 60000) return '刚刚'
        if (diff < 3600000) return Math.floor(diff / 60000) + '分钟前'
        if (diff < 86400000) return Math.floor(diff / 3600000) + '小时前'

        return date.toLocaleString('zh-CN', {
          month: '2-digit',
          day: '2-digit',
          hour: '2-digit',
          minute: '2-digit'
        })
      },

      scrollToBottom() {
        if (this.$refs.messagesContainer) {
          this.$refs.messagesContainer.scrollTop = this.$refs.messagesContainer.scrollHeight
        }
      },

      autoCollapseThinkBlocks() {
        // 自动折叠所有思考过程
        this.$nextTick(() => {
          const expandedThinkBlocks = document.querySelectorAll('.think-content:not(.collapsed)')

          if (expandedThinkBlocks.length > 0) {
            console.log(`🔽 折叠 ${expandedThinkBlocks.length} 个思考块`)
            expandedThinkBlocks.forEach(block => {
              const id = block.id.replace('content-', '')
              if (id) {
                window.toggleThinkBlock(id)
              }
            })
          }
        })
      },

      autoResizeTextarea() {
        if (this.$refs.textarea) {
          this.$refs.textarea.style.height = 'auto'
          this.$refs.textarea.style.height = this.$refs.textarea.scrollHeight + 'px'
        }
      },

      setQuickMessage(msg) {
        this.message = msg
        this.$nextTick(() => {
          if (this.$refs.textarea) {
            this.$refs.textarea.focus()
          }
        })
      },

      async rethinkMessage(messageIndex) {
        // 找到这个 AI 回答对应的用户问题
        const aiMessage = this.messages[messageIndex]
        if (aiMessage.role !== 'assistant') {
          this.$message.error('只能重新思考 AI 的回答')
          return
        }

        // 向前查找最近的用户消息
        let userMessage = null
        for (let i = messageIndex - 1; i >= 0; i--) {
          if (this.messages[i].role === 'user') {
            userMessage = this.messages[i]
            break
          }
        }

        if (!userMessage) {
          this.$message.error('未找到对应的用户问题')
          return
        }

        // 确认操作
        try {
          await this.$confirm('确定要重新思考这个问题吗？将删除当前回答并重新生成。', '提示', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          })
        } catch {
          return
        }

        // 删除 AI 回答
        this.messages.splice(messageIndex, 1)

        // 重新发送用户问题
        this.message = userMessage.content
        await this.$nextTick()
        this.handleSend()
      },

      copyMessage(content) {
        // 移除 HTML 标签和 <think> 标签，只复制纯文本
        let textContent = content
          // 移除 <think> 标签及其内容
          .replace(/<think>[\s\S]*?<\/think>/gi, '')
          // 移除 HTML 标签
          .replace(/<[^>]+>/g, '')
          // 将 HTML 实体转回普通字符
          .replace(/&lt;/g, '<')
          .replace(/&gt;/g, '>')
          .replace(/&amp;/g, '&')
          // 将 <br> 转为换行
          .replace(/<br\s*\/?>/gi, '\n')
          // 移除多余的空白
          .trim()

        // 复制到剪贴板
        if (navigator.clipboard && navigator.clipboard.writeText) {
          navigator.clipboard.writeText(textContent)
            .then(() => {
              this.$message.success('已复制到剪贴板')
            })
            .catch(() => {
              // 降级方案
              this.fallbackCopy(textContent)
            })
        } else {
          // 降级方案
          this.fallbackCopy(textContent)
        }
      },

      fallbackCopy(text) {
        const textarea = document.createElement('textarea')
        textarea.value = text
        textarea.style.position = 'fixed'
        textarea.style.opacity = '0'
        document.body.appendChild(textarea)
        textarea.select()
        try {
          document.execCommand('copy')
          this.$message.success('已复制到剪贴板')
        } catch (err) {
          this.$message.error('复制失败，请手动复制')
        }
        document.body.removeChild(textarea)
      },

      resendMessage(content) {
        if (this.isLoading) {
          this.$message.warning('请等待当前消息完成')
          return
        }

        // 直接设置到输入框
        this.message = content
        this.$nextTick(() => {
          if (this.$refs.textarea) {
            this.$refs.textarea.focus()
          }
        })
      },

      /**
       * 处理变量文件上传（单文件变量）
       */
      handleVariableFileSelect(variable, e) {
        const files = Array.from((e && e.target && e.target.files) || [])
        if (!files.length) return

        const varName = variable.variable || variable.name
        const file = files[0]

        this.$set(this.variableValues, varName, {
          uid: `${Date.now()}_${Math.random().toString(36).slice(2)}`,
          name: file.name,
          size: file.size,
          raw: file
        })

        this.$message.success(`已选择文件：${file.name}`)
        if (e.target) {
          e.target.value = ''
        }
      },

      /**
       * 处理变量文件列表选择（文件列表变量）
       */
      handleVariableFileListSelect(variable, e) {
        const files = Array.from((e && e.target && e.target.files) || [])
        if (!files.length) return

        const varName = variable.variable || variable.name

        const mapped = files.map(f => ({
          uid: `${Date.now()}_${Math.random().toString(36).slice(2)}`,
          name: f.name,
          size: f.size,
          raw: f
        }))

        // 添加到现有列表
        if (!this.variableValues[varName]) {
          this.$set(this.variableValues, varName, [])
        }
        this.$set(this.variableValues, varName, this.variableValues[varName].concat(mapped))

        this.$message.success(`已添加 ${files.length} 个文件`)
        if (e.target) {
          e.target.value = ''
        }
      },

      /**
       * 移除变量文件（单文件变量）
       */
      removeVariableFile(variable) {
        const varName = variable.variable || variable.name
        this.$set(this.variableValues, varName, null)
        this.$message.success('已移除文件')
      },

      /**
       * 移除变量文件列表中的某个文件
       */
      removeVariableFileFromList(variable, index) {
        const varName = variable.variable || variable.name
        if (this.variableValues[varName] && Array.isArray(this.variableValues[varName])) {
          const removed = this.variableValues[varName].splice(index, 1)[0]
          if (removed) {
            this.$message.success(`已移除：${removed.name}`)
          }
        }
      },

      /**
       * 从上传结果中提取 Dify 文件 ID
       * @param {Object} uploadResult - 上传结果，格式为 { code: 200, data: AiFile }
       * @returns {string|null} Dify 文件 ID
       */
      extractDifyFileId(uploadResult) {
        try {
          // uploadResult 是 ResponseResult 格式：{ code: 200, data: AiFile }
          const fileData = uploadResult?.data || uploadResult;

          // 从 difyResponse 中解析 Dify 文件 ID
          if (fileData?.difyResponse) {
            let difyResponse = null;
            if (typeof fileData.difyResponse === "string") {
              difyResponse = JSON.parse(fileData.difyResponse);
            } else {
              difyResponse = fileData.difyResponse;
            }

            // 尝试从多个可能的字段中获取文件 ID
            const fileId =
              difyResponse?.id ||
              difyResponse?.file_id ||
              difyResponse?.upload_file_id;
            if (fileId) {
              return fileId;
            }
          }

          // 如果没有 difyResponse，尝试使用数据库 ID（不推荐，但作为后备）
          return fileData?.id || null;
        } catch (error) {
          console.error("提取 Dify 文件 ID 失败:", error);
          return null;
        }
      },

      /**
       * 获取文件类型
       */
      getFileType(filename) {
        const ext = (filename.split(".").pop() || "").toLowerCase();
        const imageExts = ["jpg", "jpeg", "png", "gif", "webp", "svg"];
        const docExts = [
          "txt",
          "md",
          "pdf",
          "doc",
          "docx",
          "html",
          "xlsx",
          "xls",
          "csv",
        ];

        if (imageExts.includes(ext)) return "image";
        if (docExts.includes(ext)) return "document";
        return "custom";
      },

      /**
       * 智能检测 answer 中是否包含思考过程
       * @param {string} content - 要检测的内容
       * @returns {boolean} 是否包含思考过程
       */
      detectThinkingContent(content) {
        if (!content || typeof content !== 'string') return false

        // 检测思考过程的特征模式
        const thinkingPatterns = [
          // 中文思考模式
          /(?:我需要|我将|让我|首先|接下来|然后|最后|总结|根据|基于|通过|分析|检查|提取|确认|注意|强调)/,
          // 英文思考模式
          /(?:I need to|I will|Let me|First|Next|Then|Finally|Based on|According to|Through|By analyzing)/,
          // 步骤性描述
          /(?:步骤|步骤一|步骤二|第一步|第二步|首先|其次|最后)/,
          // 分析性描述
          /(?:分析|检查|提取|确认|验证|评估|考虑|思考)/,
          // 任务性描述
          /(?:任务|要求|需要|应该|必须|确保|保证)/
        ]

        // 检测是否包含多个思考特征
        const thinkingCount = thinkingPatterns.filter(pattern => pattern.test(content)).length

        // 检测内容长度（思考过程通常比较长）
        const isLongContent = content.length > 200

        // 检测是否包含结构化内容（如标题、列表等）
        const hasStructure = /(?:#|##|###|####|\*\*|\*|一、|二、|三、|（一）|（二）)/.test(content)

        // 综合判断：包含多个思考特征 且 (内容较长 或 有结构化内容)
        return thinkingCount >= 2 && (isLongContent || hasStructure)
      },

      /**
       * 从内容中提取思考过程和回答部分
       * @param {string} content - 原始内容
       * @returns {Object} { thinkingPart, answerPart }
       */
      extractThinkingContent(content) {
        if (!content) return { thinkingPart: null, answerPart: content }

        // 尝试多种分离策略

        // 策略1：基于标题分离（如 # 执法检查通知书）
        const titleMatch = content.match(/^(.*?)(#\s*[^#\n]+.*)$/s)
        if (titleMatch) {
          const thinkingPart = titleMatch[1].trim()
          const answerPart = titleMatch[2].trim()

          if (thinkingPart.length > 50 && this.detectThinkingContent(thinkingPart)) {
            return { thinkingPart, answerPart }
          }
        }

        // 策略2：基于分隔符分离（如 --- 或 ===）
        const separatorMatch = content.match(/^(.*?)(?:---|===|___)(.*)$/s)
        if (separatorMatch) {
          const thinkingPart = separatorMatch[1].trim()
          const answerPart = separatorMatch[2].trim()

          if (thinkingPart.length > 50 && this.detectThinkingContent(thinkingPart)) {
            return { thinkingPart, answerPart }
          }
        }

        // 策略3：基于内容特征分离
        // 查找第一个明显的"结果"或"输出"部分
        const resultPatterns = [
          /(.*?)(#\s*[^#\n]+.*)$/s,  // 标题开始
          /(.*?)(##\s*[^#\n]+.*)$/s, // 二级标题开始
          /(.*?)(\*\*[^*]+\*\*.*)$/s, // 粗体开始
          /(.*?)(一、[^一]*二、.*)$/s  // 中文序号开始
        ]

        for (const pattern of resultPatterns) {
          const match = content.match(pattern)
          if (match) {
            const thinkingPart = match[1].trim()
            const answerPart = match[2].trim()

            if (thinkingPart.length > 50 && this.detectThinkingContent(thinkingPart)) {
              return { thinkingPart, answerPart }
            }
          }
        }

        // 策略4：基于长度和内容特征智能分离
        const lines = content.split('\n')
        let thinkingLines = []
        let answerLines = []
        let foundAnswerStart = false

        for (let i = 0; i < lines.length; i++) {
          const line = lines[i]

          // 检测是否是答案开始的标志
          if (!foundAnswerStart && (
            line.match(/^#\s/) ||  // 标题
            line.match(/^##\s/) || // 二级标题
            line.match(/^\*\*[^*]+\*\*$/) || // 粗体标题
            line.match(/^[一二三四五六七八九十]+、/) || // 中文序号
            line.match(/^[1-9]+\./) // 数字序号
          )) {
            foundAnswerStart = true
            answerLines.push(line)
          } else if (foundAnswerStart) {
            answerLines.push(line)
          } else {
            thinkingLines.push(line)
          }
        }

        const thinkingPart = thinkingLines.join('\n').trim()
        const answerPart = answerLines.join('\n').trim()

        if (thinkingPart.length > 50 && answerPart.length > 20 && this.detectThinkingContent(thinkingPart)) {
          return { thinkingPart, answerPart }
        }

        // 如果无法分离，返回原内容
        return { thinkingPart: null, answerPart: content }
      },

      /**
       * 预览文件
       */
      previewFile(file) {
        if (!file) return

        // 如果有 URL，直接在新窗口打开
        if (file.url) {
          window.open(file.url, '_blank')
        } else if (file.raw) {
          // 如果有原始文件，创建临时 URL 并打开
          const url = URL.createObjectURL(file.raw)
          window.open(url, '_blank')
          // 延迟释放 URL（给浏览器时间打开）
          setTimeout(() => URL.revokeObjectURL(url), 60000)
        } else {
          this.$message.warning('文件预览不可用')
        }
      },

      toggleTheme() {
        // 主题切换功能（可选）
        console.log('主题切换')
      },

      handleLogin() {
        // 登录功能（可选）
        this.$router.push('/login')
      }
    },
    watch: {
      message() {
        this.$nextTick(() => {
          this.autoResizeTextarea()
        })
      }
    }
  }
</script>

  <style lang="scss" scoped>
  .page-wrapper {
    width: 100%;
    height: 100vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }

  .home-root {
    position: relative;
    width: 100%;
    height: calc(100% - 66px); /* 减去导航栏高度 */
    margin-top: 66px; /* 为固定导航栏留出空间 */
    display: flex;
    flex-direction: row;
  /* 更偏专业、坚定的中性蓝灰背景，整体略偏蓝，并加入少量红色呼应主色 */
  background:
    /* 左上蓝灰主光晕 */
    radial-gradient(circle at 14% 18%, rgba(148, 163, 184, 0.6) 0, rgba(148, 163, 184, 0.0) 55%),
    /* 右下冷蓝加强，整体更偏蓝 */
    radial-gradient(circle at 84% 82%, rgba(37, 99, 235, 0.38) 0, rgba(37, 99, 235, 0.0) 55%),
    /* 右上淡红晕，低透明度，只是轻微映红背景 */
    radial-gradient(circle at 82% 10%, rgba(220, 38, 38, 0.12) 0, rgba(220, 38, 38, 0.0) 52%),
    /* 底层偏蓝的线性渐变 */
    linear-gradient(135deg, #eef2ff 0%, #e5e7eb 40%, #dbeafe 100%);
    --c-primary: #3b82f6;
    --c-primary-2: #2563eb;
    --c-accent: #0ea5e9;
    --c-bg: #f1f5f9;
    --c-card: #ffffff;
    --c-border: #cbd5e1;
    --c-muted: #64748b;
    --c-law-blue: #3b82f6;
    --c-law-dark: #2563eb;
    --c-law-gray: #475569;
  }

  /* 增强玻璃拟态浮动元素 */
  .home-root::before {
    content: '';
    position: absolute;
    top: 15%;
    left: 15%;
    width: 320px;
    height: 320px;
  /* 左侧光斑：偏钢蓝灰，少量暗红混合，整体仍以蓝灰为主 */
  background: radial-gradient(circle at 30% 30%,
    rgba(30, 64, 175, 0.42) 0,
    rgba(30, 64, 175, 0.18) 45%,
    rgba(127, 29, 29, 0.12) 100%);
    border-radius: 50%;
    filter: blur(80px);
    opacity: 0.35;
    z-index: 0;
    animation: float 20s infinite alternate ease-in-out;
    pointer-events: none;
  }

  .home-root::after {
    content: '';
    position: absolute;
    bottom: 15%;
    right: 15%;
    width: 240px;
    height: 240px;
  /* 右侧光斑：冷蓝为主，略带灰度，保证专业质感 */
  background: radial-gradient(circle at 70% 70%,
    rgba(37, 99, 235, 0.46) 0,
    rgba(59, 130, 246, 0.30) 50%,
    rgba(148, 163, 184, 0.20) 100%);
    border-radius: 50%;
    filter: blur(80px);
    opacity: 0.35;
    z-index: 0;
    animation: float 20s infinite alternate ease-in-out;
    animation-delay: -5s;
    pointer-events: none;
  }

  @keyframes float {
    0% {
      transform: translate(0, 0) scale(1);
    }
    100% {
      transform: translate(50px, 30px) scale(1.1);
    }
  }

  .home-root > .sidebar,
  .home-root > .main-content {
    position: relative;
    z-index: 1;
  }

/* ========== 侧边栏配色优化（参考执法检查助手，偏专业蓝白） ========== */
/* 只给用户视图左侧侧边栏外框的右上角加圆角，其他角保持不变 */
::v-deep .sidebar {
  border-top-right-radius: 16px;
  overflow: hidden; /* 避免子元素背景“刺出”圆角 */
}

/* 侧边栏整体背景：以白+极淡暖粉为主，融到中间主卡片的暖色体系里 */
::v-deep .history-drawer {
  background: linear-gradient(
    180deg,
    #ffffff 0%,
    #fdf2f2 38%,
    #f9fafb 100%
  ) !important;
  border-top-right-radius: 16px !important;
  overflow: hidden !important;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Microsoft YaHei', sans-serif !important;
}

/* 侧边栏顶部标题区域：统一为极淡蓝色 */
::v-deep .drawer-header {
  background: #f0f6ff !important;
  border-bottom: 1px solid rgba(148, 163, 184, 0.15) !important;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Microsoft YaHei', sans-serif !important;
}

/* 历史列表整体背景 */
::v-deep .drawer-content {
  background: linear-gradient(
    180deg,
    rgba(240, 246, 255, 0.60) 0%,
    rgba(248, 250, 255, 0.70) 40%,
    rgba(240, 246, 255, 0.65) 100%
  ) !important;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Microsoft YaHei', sans-serif !important;
}

/* 新建对话按钮区域：白底红边，避免过度抢眼，又和页面红主色呼应 */
::v-deep .quick-action-bar {
  background: transparent !important;
}

::v-deep .quick-new-chat-btn {
  background: linear-gradient(
    135deg,
    #ffffff 0%,
    #ffffff 60%,
    #fff7f7 100%
  ) !important;
  border-radius: 18px !important;
  border: 1px solid rgba(248, 113, 113, 0.45) !important;
  box-shadow: 0 2px 8px rgba(248, 113, 113, 0.12) !important;
  color: #b91c1c !important;
}

::v-deep .quick-new-chat-btn h3 {
  color: #b91c1c !important;
}

/* 分组标题 */
::v-deep .group-label {
  color: #0f172a !important;
  opacity: 0.75;
}

/* 历史项卡片 */
::v-deep .history-item {
  background: rgba(255, 255, 255, 0.78) !important;
  border-radius: 12px !important;
  border: 1px solid rgba(148, 163, 184, 0.18) !important;
  transition: all 0.25s ease !important;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Microsoft YaHei', sans-serif !important;
}

::v-deep .history-item:hover {
  background: rgba(255, 255, 255, 0.95) !important;
  border-color: rgba(248, 113, 113, 0.5) !important;
  box-shadow: 0 4px 14px rgba(248, 113, 113, 0.15) !important;
}

/* 当前选中的历史项 */
::v-deep .history-item.active {
  background: linear-gradient(
    135deg,
    rgba(248, 113, 113, 0.10) 0%,
    rgba(248, 250, 252, 1) 100%
  ) !important;
  border-color: rgba(185, 28, 28, 0.8) !important;
  box-shadow: 0 6px 18px rgba(248, 113, 113, 0.20) !important;
}

/* 浮动工具栏背景 - pill 按钮：改为白底+红边的小胶囊，避免大块高饱和蓝色 */
::v-deep .pill {
  background: rgba(255, 255, 255, 0.96) !important;
  border-radius: 999px !important;
  border: 1px solid rgba(248, 113, 113, 0.45) !important;
  box-shadow: 0 4px 14px rgba(15, 23, 42, 0.12) !important;
  color: #b91c1c !important;
  backdrop-filter: blur(8px) !important;
}

  /* 主内容区样式 */
  .main-content {
    flex: 1;
    height: 100%;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding: 20px;
    box-sizing: border-box;
  }

  /* 聊天容器 - 大框体样式 */
  .chat-container {
    flex: 1;
    background: rgba(255, 255, 255, 0.78);
    backdrop-filter: blur(14px) saturate(130%);
    -webkit-backdrop-filter: blur(14px) saturate(130%);
    border-radius: 24px;
    border: 1px solid rgba(255, 255, 255, 0.85);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    height: 100%;
    max-width: 1200px;
    margin: 0 auto;
    width: 100%;
  }

  /* 欢迎区域 - 聊天气泡样式 */
  .welcome-area {
    flex: 1 1 auto;
    min-height: auto;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    position: relative;
    padding-top: 80px;
    padding-bottom: 28px;
    padding-left: 28px;
    padding-right: 28px;
    overflow-y: auto;
  }

  /* 聊天区域 */
  .chat-area {
    flex: 1 1 auto;
    min-height: 300px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    position: relative;
    padding: 28px;
  }

  .welcome-chat-container {
    width: 95%;
    max-width: 1500px;
    margin: 0 auto;
    padding: 0;
    animation: fadeIn 0.6s ease;
  }

  /* 欢迎页整体字体：统一为系统无衬线，不影响聊天区仿宋字体 */
  .welcome-area,
  .welcome-area .welcome-chat-container,
  .welcome-area .welcome-hero,
  .welcome-area .capability-banner,
  .welcome-area .feature-dashboard,
  .welcome-area .kb-section {
    font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
  }

  /* 欢迎页 - 征信合规智能专家布局（参考 1.html） */
.welcome-hero {
  padding-bottom: 32px;
  /* 局部颜色变量（只作用于欢迎页） */
  --credit-primary: #b91c1c;
  --credit-primary-light: #fca5a5;
  --credit-primary-bg: #fef2f2;
  --credit-text-main: #1e293b;
  --credit-text-secondary: #64748b;
}

.capability-banner {
  background: linear-gradient(135deg, #ffffff 0%, var(--credit-primary-bg) 100%);
  border-radius: 16px;
  border: 1px solid #fecaca;
  padding: 30px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 30px;
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05),
    0 2px 4px -1px rgba(0, 0, 0, 0.03);
  position: relative;
  overflow: hidden;
}

.capability-banner::after {
  content: 'ℹ';
  position: absolute;
  right: -20px;
  bottom: -30px;
  font-size: 10rem;
  color: var(--credit-primary);
  opacity: 0.03;
  transform: rotate(-15deg);
}

.banner-text {
  flex: 1;
  z-index: 2;
}

.banner-title {
  font-size: 1.6rem;
  font-weight: 800;
  color: var(--credit-primary);
  margin-bottom: 10px;
  letter-spacing: 1px;
}

.banner-desc {
  color: var(--credit-text-secondary);
  font-size: 1rem;
  margin-bottom: 20px;
  max-width: 600px;
  line-height: 1.6;
}

/* 开场问题容器（精细化样式：标题 + 胶囊标签，整体更轻盈） */
.banner-opening-questions {
  margin-top: 16px;
  padding: 0;
  border-radius: 0;
  background: transparent;
  border: none;
  max-width: 680px;
}

.opening-questions-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}

.opening-questions-label {
  font-size: 12px;
  font-weight: 700;
  color: #b91c1c;
  letter-spacing: 0.2em;
  position: relative;
  padding-left: 10px;
}

.opening-questions-label::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 4px;
  height: 12px;
  border-radius: 999px;
  background: linear-gradient(180deg, #fb7185, #f97373);
}

.opening-questions-count {
  font-size: 12px;
  color: #9ca3af;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(248, 250, 252, 0.9);
  border: 1px solid rgba(226, 232, 240, 0.9);
}

.opening-questions-list {
  display: flex;
  flex-wrap: wrap;
  /* 使用 margin 模拟 gap，兼容 Chrome 75 等不支持 flex-gap 的浏览器 */
  margin: -5px;
}

.opening-question-item {
  display: flex;
  align-items: center;
  border-radius: 999px;
  border: 1px solid rgba(254, 202, 202, 0.9);
  background: rgba(255, 255, 255, 0.95);
  padding: 6px 14px;
  /* 统一使用 margin 控制间距，避免依赖 flex-gap */
  margin: 5px;
  cursor: pointer;
  transition: all 0.2s ease;
  font-size: 14px;
  color: #374151;
  max-width: 100%;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
  box-shadow: 0 4px 10px rgba(248, 113, 113, 0.04);
}

.opening-question-item:hover {
  border-color: #f97373;
  box-shadow: 0 6px 18px rgba(248, 113, 113, 0.18);
  transform: translateY(-1px);
  background: linear-gradient(135deg, #fff7f7, #ffffff);
}

.opening-question-item .q-index {
  display: none;
}

.opening-question-item .q-text {
  flex: 1;
  text-align: left;
  padding-right: 4px;
}

.tag-group {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}

.cap-tag {
  background: #ffffff;
  border: 1px solid var(--credit-primary-light);
  color: var(--credit-primary);
  padding: 6px 12px;
  border-radius: 6px;
  font-size: 0.85rem;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 6px;
  box-shadow: 0 2px 4px rgba(185, 28, 28, 0.05);
}

.cap-tag i {
  font-size: 14px;
}

.banner-img-box {
  width: 180px;
  height: 180px;
  background: #ffffff;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 6px solid var(--credit-primary-bg);
  box-shadow: 0 16px 40px rgba(185, 28, 28, 0.25);
  z-index: 2;
  margin-left: 20px; /* 往左移动一些，露出右侧淡淡图案 */
  flex-shrink: 0;
}

.banner-avatar-image {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
}

.section-label {
  font-size: 0.9rem;
  font-weight: 700;
  color: #334155;
  margin-bottom: 16px;
  padding-left: 10px;
  border-left: 4px solid var(--credit-primary);
}

.feature-dashboard {
  display: grid;
  grid-template-columns: 320px 1fr;
  /* 大屏两列布局，右侧整体与左侧主卡片之间保持固定列间距。
     使用 column-gap 以确保在旧版浏览器中也有较好的兼容性。 */
  column-gap: 24px;
  margin-bottom: 30px;
}

.main-card {
  background: #ffffff;
  border-radius: 16px;
  border: 1px solid #e2e8f0;
  padding: 24px;
  cursor: pointer;
  transition: all 0.3s;
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05),
    0 2px 4px -1px rgba(0, 0, 0, 0.03);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  position: relative;
  overflow: hidden;
}

.main-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 10px 15px -3px rgba(185, 28, 28, 0.2),
    0 4px 6px -2px rgba(0, 0, 0, 0.05);
  border-color: var(--credit-primary);
}

.main-card-header {
  display: flex;
  /* 让左侧图标与右侧标题垂直居中对齐，避免视觉上“头重脚轻” */
  align-items: center;
  justify-content: space-between;
  margin-bottom: 16px;
}

.main-icon {
  font-size: 2.5rem;
  color: var(--credit-primary);
}

.main-card h3 {
  font-size: 1.2rem;
  margin-bottom: 8px;
  color: var(--credit-text-main);
}

.main-card p {
  font-size: 0.9rem;
  color: var(--credit-text-secondary);
  line-height: 1.5;
}

.main-btn {
  margin-top: 20px;
  background: var(--credit-primary-bg);
  color: var(--credit-primary);
  padding: 10px;
  border-radius: 6px;
  text-align: center;
  font-weight: 600;
  font-size: 0.9rem;
  transition: all 0.2s;
}

.main-card:hover .main-btn {
  background: var(--credit-primary);
  color: #ffffff;
}

.sub-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  /* 使用负 margin + 子项 margin 模拟 gap，兼容不支持 grid-gap 的旧版浏览器 */
  margin: -10px;
}

.sub-card {
  background: #ffffff;
  border-radius: 16px;
  border: 1px solid #e2e8f0;
  padding: 18px;
  cursor: pointer;
  transition: all 0.2s;
  display: flex;
  /* 顶部对齐，让图标顶部与文字顶部在同一水平线 */
  align-items: flex-start;
  /* 不再依赖 gap，使用 margin 控制内部间距，兼容旧版浏览器 */
  gap: 0;
  /* 子卡片之间的间距（与 .sub-grid 的负 margin 配合使用），避免卡片贴在一起 */
  margin: 10px;
}

.sub-card:hover {
  border-color: var(--credit-primary);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}

.sub-icon-box {
  width: 48px;
  height: 48px;
  background: #f8fafc;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.3rem;
  color: #64748b;
  transition: all 0.2s;
  /* 明确增加与文字块的水平间距，确保在 Chrome 75 等旧版浏览器中也不会贴得太近 */
  margin-right: 20px;
  /* 不再额外位移，交给 flex-start 统一控制两者的顶部对齐 */
  margin-top: 0;
}

.sub-card:hover .sub-icon-box {
  background: var(--credit-primary);
  color: #ffffff;
}

.sub-info {
  /* 让文字区域整体相对图标稍微下移一点，避免贴着顶部 */
  margin-top: 4px;
}

.sub-info h4 {
  font-size: 1rem;
  font-weight: 700;
  /* 顶部和底部各留 4px，让标题略微下移一点，视觉更舒适 */
  margin: 4px 0 4px 0;
  color: var(--credit-text-main);
}

.sub-info p {
  font-size: 0.8rem;
  color: #94a3b8;
  margin: 0;
}

.kb-section {
  background: #ffffff;
  border-radius: 16px;
  border: 1px solid #e2e8f0;
  padding: 20px 24px;
  display: flex;
  flex-direction: column;

}

.kb-header {
  font-size: 0.9rem;
  font-weight: 700;
  color: #334155;
  display: flex;
  align-items: center;
  margin-bottom: 16px;

}

.kb-header i {
  color: var(--credit-primary);
  margin-right: 8px;
}

.kb-tags-container {
  display: flex;
  flex-wrap: wrap;
  /* 与标题拉开一点垂直距离，避免第一个标签贴在标题下面 */
  margin-top: 10px;
}

.kb-tag {
  background: #f8fafc;
  border: 1px solid #e2e8f0;
  padding: 8px 14px;
  border-radius: 20px;
  font-size: 0.85rem;
  color: #475569;
  display: flex;
  align-items: center;

  cursor: default;
  /* 每个标签自身留出间距，兼容不支持 flex-gap 的浏览器 */
  margin: 0 10px 10px 0;
}

.kb-tag:hover {
  border-color: #cbd5e1;
  background: #f1f5f9;
}

.kb-tag i {
  color: var(--credit-primary);
  opacity: 0.7;
  /* 显式增加图标与文字的水平间距，避免在老版本 Chrome 下贴得太近 */
  margin-right: 8px;
}

.kb-tag-more {
  background: #ffffff;
  border-style: dashed;
  color: #9ca3af;
}

/* 欢迎页中变量提示的收窄样式 */
.welcome-variable-tip-inline {
  margin-top: 18px;
  max-width: 100%;
}

@media (max-width: 1024px) {
  .feature-dashboard {
    grid-template-columns: 1fr;
  }

  .banner-img-box {
    margin-left: 8px;
    width: 140px;
    height: 140px;
  }
}

@media (max-width: 768px) {
  .capability-banner {
    flex-direction: column;
    align-items: flex-start;
  }

  .banner-img-box {
    margin: 16px auto 0;
  }

  .sub-grid {
    grid-template-columns: 1fr;
  }
}

  .welcome-message-wrapper {
    display: flex;
    justify-content: center;
  }

  .welcome-message-item {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-align: start;
    -ms-flex-align: start;
    align-items: flex-start;
    width: 100%;
    justify-content: center;
  }

  /* 欢迎头像 */
  .welcome-avatar {
    position: relative;
    width: 120px;
    height: 120px;
    border-radius: 50%;
    background: linear-gradient(135deg, #8d9ee9 0%, #46d8d6 100%);
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);
    animation: float 6s ease-in-out infinite;
  }

  .welcome-avatar-img {
    width: 117px;
    height: 117px;
    border-radius: 50%;
    object-fit: cover;
    border: 4px solid rgba(255, 255, 255, 0.9);
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
  }

  /* 欢迎气泡 */
  .welcome-bubble {
    background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
    border-radius: 24px;
    padding: 40px 40px;
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.06),
    inset 0 0 0 1px rgba(255, 255, 255, 0.5);
    transition: all 0.3s ease;
    width: 100%;
    max-width: 1000px;
    min-height: 240px;
    display: flex;
    flex-direction: column;
  }

  .welcome-bubble > * + * {
    margin-top: 28px;
  }

  /* 上半部分：卡通形象和欢迎语并排 */
  .welcome-top-section {
    display: flex;
    align-items: center;
    text-align: left;
    width: 100%;
    gap: 32px;
  }

  .welcome-top-section > * + * {
    margin-left: 32px;
  }

  /* 左侧卡通形象区域 */
  .welcome-avatar-section {
    flex-shrink: 0;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-right: 32px;
  }

  .welcome-avatar-section > * + * {
    margin-left: 12px;
  }

  .welcome-avatar {
    position: relative;
    width: 120px;
    height: 120px;
    border-radius: 50%;
    background: linear-gradient(135deg, #8d9ee9 0%, #46d8d6 100%);
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);
    animation: float 6s ease-in-out infinite;
  }

  .welcome-avatar-image {
    width: 117px;
    height: 117px;
    border-radius: 50%;
    object-fit: cover;
    border: 4px solid rgba(255, 255, 255, 0.9);
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
  }

  /* 右侧欢迎语区域 */
  .welcome-greeting-section {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-orient: vertical;
    -webkit-box-direction: normal;
    -ms-flex-direction: column;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    flex: 1;
  }

  .welcome-greeting-section > * + * {
    margin-top: 16px;
  }

  .welcome-greeting {
    font-size: 28px;
    font-weight: 700;
    background: linear-gradient(135deg, #0d47a1 0%, #1976d2 50%, #42a5f5 100%);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    line-height: 1.4;
    text-align: left;
    margin: 0;
    letter-spacing: -0.3px;
    text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  }

  .welcome-subtitle {
    font-size: 16px;
    color: #666;
    line-height: 1.5;
    text-align: left;
    max-width: 100%;
    margin: 0;
    font-weight: 400;
  }

  /* 分隔线 */
  .welcome-divider {
    height: 1px;
    background: linear-gradient(
        to right,
        transparent,
        rgba(13, 71, 161, 0.15),
        transparent
    );
    margin: 8px 0;
  }

  /* 下半部分：提示问题和推荐问题 */
  .welcome-bottom-section {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    width: 100%;
  }

  .welcome-bottom-section > * + * {
    margin-top: 24px;
  }

  .welcome-tip-section {
    display: flex;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    font-size: 16px;
    color: #666;
    padding: 12px 0;
    text-align: left;
    align-self: flex-start;
  }

  .welcome-tip-section > * + * {
    margin-left: 8px;
  }

  .welcome-tip-section i {
    font-size: 14px;
    color: #0d47a1;
  }

  /* 欢迎标题 */
  .welcome-bubble-header {
    margin-bottom: 0;
  }

  .welcome-greeting {
    font-size: 28px;
    font-weight: 700;
    background: linear-gradient(135deg, #0d47a1 0%, #1976d2 50%, #42a5f5 100%);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    line-height: 1.4;
    text-align: left;
    margin: 0;
    letter-spacing: -0.3px;
    text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  }

  /* 开场白内容 */
  .welcome-bubble-content {
    margin-bottom: 0;
    line-height: 1.75;
  }

  .welcome-bubble-content p {
    margin: 0;
    font-size: 16px;
    color: #333;
    line-height: 1.75;
  }

  /* 提示文字 */
  .welcome-bubble-tip {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 0;
    padding-bottom: 12px;
    border-bottom: 1px solid rgba(13, 71, 161, 0.08);
    font-size: 14px;
    color: #666;
  }

  .welcome-content-section .welcome-bubble-tip {
    text-align: left;
    justify-content: flex-start;
  }

  .welcome-bubble-tip i {
    font-size: 16px;
    color: #0D47A1;
  }

  .welcome-bubble-tip span {
    font-size: 14px;
    color: #666;
  }

  /* 推荐问题按钮 */
  .welcome-suggestions {
    display: -ms-grid;
    display: grid;
    -ms-grid-columns: (minmax(280px, 1fr))[auto-fit];
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    grid-gap: 12px;
    gap: 12px;
    width: 100%;
    max-width: 100%;
  }

  .welcome-suggestion-btn {
    display: flex;
    align-items: center;
    padding: 14px 18px;
    background: rgba(255, 255, 255, 0.8);
    border: 1px solid rgba(13, 71, 161, 0.15);
    border-radius: 12px;
    cursor: pointer;
    transition: all 0.3s ease;
    text-align: left;
    width: 100%;
    position: relative;
    overflow: hidden;
  }

  .welcome-suggestion-btn > * + * {
    margin-left: 12px;
  }

  .welcome-suggestion-btn::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 4px;
    height: 100%;
    background: linear-gradient(135deg, #0D47A1, #1976D2);
    transform: scaleY(0);
    transition: transform 0.3s ease;
  }

  .welcome-suggestion-btn:hover {
    background: rgba(255, 255, 255, 0.95);
    border-color: rgba(13, 71, 161, 0.3);
    transform: translateY(-2px);
    box-shadow: 0 6px 20px rgba(13, 71, 161, 0.15);
  }

  .welcome-suggestion-btn:hover::before {
    transform: scaleY(1);
  }

  .welcome-suggestion-btn i {
    font-size: 18px;
    color: #0D47A1;
    flex-shrink: 0;
  }

  .welcome-suggestion-btn .suggestion-icon {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    display: block;
  }

  .welcome-suggestion-btn span {
    font-size: 14px;
    color: #333;
    font-weight: 500;
    flex: 1;
    line-height: 1.4;
  }

  /* 变量提示 */
  .welcome-variable-tip {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 12px 16px;
    background: rgba(255, 243, 224, 0.6);
    border: 1px solid rgba(255, 152, 0, 0.2);
    border-radius: 8px;
    margin-top: 16px;
  }

  .welcome-variable-tip i {
    font-size: 16px;
    color: #F57C00;
    flex-shrink: 0;
  }

  .welcome-variable-tip span {
    font-size: 14px;
    color: #E65100;
    line-height: 1.5;
  }

  /* 响应式 */
  @media (max-width: 768px) {
    .welcome-chat-container {
      padding: 0 16px;
    }

    .welcome-message-item {
      max-width: 100%;
    }

    .welcome-bubble {
      padding: 20px 22px;
    }

    .welcome-main-layout {
      flex-direction: column;
      gap: 20px;
      align-items: center;
    }

    .welcome-image-section {
      width: 100%;
      align-items: center;
    }

    .welcome-image {
      width: 160px;
      height: 160px;
    }

    .welcome-content-section {
      width: 100%;
    }

    .welcome-greeting {
      font-size: 16px;
      text-align: center;
    }

    .welcome-bubble-header {
      text-align: center;
    }

    .welcome-content-section .welcome-bubble-tip {
      text-align: center;
      justify-content: center;
    }

    .welcome-bubble-content p {
      font-size: 15px;
    }
  }

  /* 聊天区域 */
  .chat-area {
    flex: 1;
    min-height: 300px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    position: relative;
    padding: 28px;
  }

  .chat-area::before {
    content: '';
    position: absolute;
    inset: 0;
    background: rgba(255, 255, 255, 0);
    z-index: 0;
    pointer-events: none;
  }

  .messages-container {
    flex: 1;
    overflow-y: auto;
    padding: 68px 0 28px; /* 增加顶部内边距 */
    padding-bottom: 140px; /* 为固定输入框预留空间，避免消息被遮挡 */
    display: flex;
    flex-direction: column;
    align-items: center;
    position: relative;
    z-index: 1;

    /* 自定义滚动条 */
    &::-webkit-scrollbar {
      width: 10px;
    }

    &::-webkit-scrollbar-track {
      background: transparent;
    }

    &::-webkit-scrollbar-thumb {
      background: rgba(255, 255, 255, 0.3);
      border-radius: 5px;
    }

    &::-webkit-scrollbar-thumb:hover {
      background: rgba(255, 255, 255, 0.5);
    }
  }

  .message-wrapper {
    display: flex;
    margin-bottom: 28px;
    animation: fadeIn 0.6s ease;
    width: 100%;
    max-width: 900px;
    padding: 0 24px;
    box-sizing: border-box;
  }

  .message-wrapper.user {
    justify-content: flex-end;
  }

  .message-wrapper.assistant {
    justify-content: flex-start;
  }

  @keyframes fadeIn {
    from {
      opacity: 0;
      transform: translateY(15px);
    }
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }

  .message-item {
    display: flex;
    max-width: 85%;
    min-width: 200px;
    align-items: flex-start;
    width: fit-content;
  }

  /* 响应式设计：超大屏幕时限制宽度 */
  @media (min-width: 1400px) {
    .message-wrapper {
      max-width: 1000px;
    }

    .message-item {
      max-width: 80%;
    }
  }

  /* 中等屏幕 */
  @media (max-width: 768px) {
    .message-wrapper {
      max-width: 100%;
      padding: 0 16px;
    }

    .message-item {
      max-width: 90%;
      min-width: auto;
    }
  }

  .message-avatar {
    width: 52px;
    height: 52px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    font-size: 20px;
    background: rgba(255, 255, 255, 0.75);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    border: 1px solid rgba(255, 255, 255, 0.85);
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.08);
    overflow: hidden;
    margin-right: 28px;
  }

  .message-avatar .avatar-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .message-wrapper.user .message-avatar {
    background: linear-gradient(135deg, rgba(13, 71, 161, 0.85), rgba(25, 118, 210, 0.7));
    color: #ffffff;
    margin-left: 28px;
    margin-right: 0;
  }

  .message-wrapper.assistant .message-avatar {
    background: rgba(255, 255, 255, 0.75);
    color: #3b82f6;
  }

  .message-content {
    flex: 0 1 auto;
    min-width: 0;
    width: fit-content;
  }

  .message-text {
    padding: 20px 25px;
    border-radius: 24px;
    line-height: 1.75;
    word-wrap: break-word;
    word-break: break-word;
    font-size: 16px;
    width: fit-content;
    max-width: 100%;
    min-width: 120px;
    height: auto;
    min-height: auto;
    transition: all 0.3s ease;
  }

  .message-wrapper.user .message-text {
    background: rgba(255, 255, 255, 0.85);
    backdrop-filter: blur(14px) saturate(120%);
    -webkit-backdrop-filter: blur(14px) saturate(120%);
    color: #333;
    border-top-right-radius: 8px;
    border: 2px solid rgba(13, 71, 161, 0.15);
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.06),
    inset 0 0 0 1px rgba(255, 255, 255, 0.5);
  }

  .message-wrapper.assistant .message-text {
    background: rgba(255, 255, 255, 0.85);
    backdrop-filter: blur(14px) saturate(120%);
    -webkit-backdrop-filter: blur(14px) saturate(120%);
    color: #333;
    border: 2px solid rgba(13, 71, 161, 0.12);
    border-top-left-radius: 8px;
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.06),
    inset 0 0 0 1px rgba(255, 255, 255, 0.5);
  }

  .message-wrapper.user .message-text ::v-deep strong {
    color: #1e40af;
    font-weight: 600;
  }

  .message-wrapper.assistant .message-text ::v-deep strong {
    color: #111827;
    font-weight: 600;
  }

  .message-text ::v-deep .code-block {
    background: #f8fafc;
    color: #334155;
    padding: 20px 24px;
    border-radius: 8px;
    margin: 12px 0;
    overflow-x: auto;
    font-family: var(--font-family-code);
    font-size: var(--font-size-text-normal);
    line-height: var(--line-height-relaxed);
    border: 1px solid #e2e8f0;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
  }

  .message-text ::v-deep .inline-code {
    background: rgba(59, 130, 246, 0.1);
    padding: 2px 6px;
    border-radius: 4px;
    font-family: var(--font-family-code);
    font-size: var(--font-size-text-small);
    color: #1e40af;
  }

  .message-wrapper.assistant .message-text ::v-deep .inline-code {
    background: #f1f5f9;
    color: #e11d48;
  }

  .message-text ::v-deep ul {
    margin: 8px 0;
    padding-left: 20px;
  }

  .message-text ::v-deep li {
    margin: 4px 0;
  }

  /* Markdown 表格样式 - 现代蓝色主题 */
  .message-text ::v-deep .markdown-table-wrapper {
    margin: 12px 0;
    overflow-x: auto;
    border-radius: 12px;
    box-shadow: 0 4px 16px rgba(59, 130, 246, 0.12);
    border: 1px solid rgba(59, 130, 246, 0.1);
  }

  .message-text ::v-deep .markdown-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    background: #ffffff;
    font-size: 14px;
    line-height: 1.6;
  }

  .message-text ::v-deep .markdown-table thead {
    background-color: #3b82f6;
    color: #ffffff;
  }

  .message-text ::v-deep .markdown-table thead th {
    padding: 12px 16px;
    text-align: center;
    font-weight: 600;
    font-size: 14px;
    white-space: nowrap;
  }

  .message-text ::v-deep .markdown-table thead th:first-child {
    border-top-left-radius: 12px;
  }

  .message-text ::v-deep .markdown-table thead th:last-child {
    border-top-right-radius: 12px;
  }

  .message-text ::v-deep .markdown-table tbody tr {
    transition: all 0.3s ease;
    border-bottom: 1px solid #e5e7eb;
  }

  .message-text ::v-deep .markdown-table tbody tr:nth-child(odd) {
    background-color: #ffffff;
  }

  .message-text ::v-deep .markdown-table tbody tr:nth-child(even) {
    background-color: #f8fafc;
  }

  .message-text ::v-deep .markdown-table tbody tr:hover {
    background: linear-gradient(90deg, #eff6ff 0%, #dbeafe 100%);
    transform: scale(1.01);
    box-shadow: 0 2px 8px rgba(59, 130, 246, 0.1);
  }

  .message-text ::v-deep .markdown-table td,
  .message-text ::v-deep .markdown-table tbody th {
    padding: 12px 16px;
    border-bottom: 1px solid #e5e7eb;
    color: #1f2937;
    vertical-align: middle;
    text-align: center;
    transition: all 0.2s ease;
  }

  .message-text ::v-deep .markdown-table tbody th {
    font-weight: 600;
    background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
    color: #1e40af;
    text-align: center;
    border-right: 3px solid #3b82f6;
  }

  .message-text ::v-deep .markdown-table tbody tr:last-child td,
  .message-text ::v-deep .markdown-table tbody tr:last-child th {
    border-bottom: none;
  }

  .message-text ::v-deep .markdown-table tbody tr:last-child td:first-child {
    border-bottom-left-radius: 12px;
  }

  .message-text ::v-deep .markdown-table tbody tr:last-child td:last-child {
    border-bottom-right-radius: 12px;
  }

  /* 用户消息中的表格样式调整 */
  .message-wrapper.user .message-text ::v-deep .markdown-table-wrapper {
    box-shadow: 0 4px 16px rgba(59, 130, 246, 0.18);
    border: 1px solid rgba(59, 130, 246, 0.2);
  }

  .message-wrapper.user .message-text ::v-deep .markdown-table thead {
    background-color: #2563eb;
  }

  .message-wrapper.user .message-text ::v-deep .markdown-table tbody tr:hover {
    background: linear-gradient(90deg, #dbeafe 0%, #bfdbfe 100%);
  }

  /* 响应式表格 */
  @media (max-width: 768px) {
    .message-text ::v-deep .markdown-table-wrapper {
      font-size: 12px;
      border-radius: 8px;
    }

    .message-text ::v-deep .markdown-table thead th,
    .message-text ::v-deep .markdown-table td,
    .message-text ::v-deep .markdown-table tbody th {
      padding: 10px 12px;
    }

    .message-text ::v-deep .markdown-table tbody tr:hover {
      transform: none;
    }
  }

  /* 思考过程样式 */
  .message-text ::v-deep .think-block {
    background: #f9fafb;
    border-left: 3px solid #9ca3af;
    border-radius: 6px;
    padding: 6px 12px;
    margin-bottom: 2px;
    animation: thinkFadeIn 0.3s ease;
    font-family: var(--font-family-fangsong);
  }

  @keyframes thinkFadeIn {
    from {
      opacity: 0;
      transform: translateY(-5px);
    }
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }

  /* 思考过程标题样式 */
  .message-text ::v-deep .thinking-section .think-header {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 12px;
    padding-bottom: 8px;
    border-bottom: 1px solid #e5e7eb;
    font-size: 13px;
    font-weight: 600;
    color: #6b7280;
    cursor: pointer;
    transition: all 0.2s ease;
  }

  .message-text ::v-deep .thinking-section .think-header:hover {
    color: #4b5563;
    background: rgba(0, 0, 0, 0.02);
    border-radius: 6px;
    padding: 4px 8px;
    margin: -4px -8px 8px -8px;
  }

  .message-text ::v-deep .thinking-section .think-header i:first-child {
    font-size: 16px;
    color: #9ca3af;
  }

  .message-text ::v-deep .thinking-section .think-toggle-icon {
    font-size: 14px;
    color: #9ca3af;
    transition: transform 0.2s ease;
    margin-left: auto;
  }

  /* 思考内容样式 */
  .message-text ::v-deep .thinking-section .think-content {
    font-size: 16px;
    line-height: 1.6;
    color: #6b7280;
    transition: all 0.3s ease;
    overflow: hidden;
    animation: fadeInUp 0.3s ease-out;
  }

  .message-text ::v-deep .thinking-section .think-content.collapsed {
    max-height: 0;
    opacity: 0;
    margin-bottom: 0;
  }

  .message-text ::v-deep .thinking-section .think-content:not(.collapsed) {
    max-height: 1000px; /* 足够大的值以容纳内容 */
    opacity: 1;
  }

  /* 用户消息中的思考块样式（如果有的话）*/
  .message-wrapper.user .message-text ::v-deep .think-block {
    background: rgba(59, 130, 246, 0.08);
    border-left-color: #3b82f6;
  }

  .message-wrapper.user .message-text ::v-deep .think-header {
    color: #1e40af;
  }

  .message-wrapper.user .message-text ::v-deep .think-header:hover {
    color: #3b82f6;
    background: rgba(59, 130, 246, 0.1);
  }

  .message-wrapper.user .message-text ::v-deep .think-header i:first-child {
    color: #3b82f6;
    font-size: 18px;
  }

  .message-wrapper.user .message-text ::v-deep .think-toggle-icon {
    color: #3b82f6;
    font-size: 14px;
  }

  .message-wrapper.user .message-text ::v-deep .think-content {
    color: #2563eb;
    font-family: var(--font-family-fangsong);
  }

  /* 正文内容样式 */
  .message-text ::v-deep .main-content {
    font-size: var(--font-size-content-primary);
    line-height: var(--line-height-content);
    color: #1f2937;
    font-weight: var(--font-weight-normal);
    font-family: var(--font-family-fangsong);
    margin: 0;
    padding: 0;
    height: auto !important;
    min-height: auto !important;
    max-height: none !important;
    display: block;
    overflow: visible;
  }

  .message-wrapper.user .message-text ::v-deep .main-content {
    color: #1e40af;
    font-family: var(--font-family-fangsong);
  }

  /* 强制正文内容容器高度贴合 */
  .message-text ::v-deep .main-content * {
    margin: 0 !important;
    padding: 0 !important;
    height: auto !important;
    min-height: auto !important;
    max-height: none !important;
    line-height: var(--line-height-content) !important;
  }

  /* 表格例外：允许表格保持自己的间距 */
  .message-text ::v-deep .main-content .markdown-table-wrapper {
    margin: 12px 0 !important;
  }

  .message-text ::v-deep .main-content .markdown-table,
  .message-text ::v-deep .main-content .markdown-table * {
    margin: 0 !important;
    padding: initial !important;
    line-height: 1.6 !important;
  }

  .message-text ::v-deep .main-content .markdown-table thead th {
    padding: 12px 16px !important;
    text-align: center !important;
  }

  .message-text ::v-deep .main-content .markdown-table td,
  .message-text ::v-deep .main-content .markdown-table tbody th {
    padding: 10px 16px !important;
    text-align: center !important;
  }

  .message-text ::v-deep .main-content p {
    margin: 0 !important;
    padding: 0 !important;
    line-height: var(--line-height-content) !important;
  }

  .message-text ::v-deep .main-content br {
    margin: 0 !important;
    padding: 0 !important;
    line-height: var(--line-height-content) !important;
  }

  .message-time {
    font-size: var(--font-size-text-mini);
    color: #9ca3af;
    margin-top: 4px;
    padding: 0 4px;
  }

  .message-wrapper.user .message-time {
    text-align: right;
  }

  /* 消息操作按钮区域 */
  .message-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
    margin-top: 6px;
  }

  .action-buttons {
    display: flex;
    align-items: center;
    gap: 2px;
    opacity: 0;
    transition: opacity 0.2s;
  }

  .message-wrapper:hover .action-buttons {
    opacity: 1;
  }

  .action-btn {
    width: 28px;
    height: 28px;
    padding: 0;
    background: transparent;
    border: 1px solid #e5e7eb;
    border-radius: 6px;
    color: #6b7280;
    font-size: 14px;
    cursor: pointer;
    transition: all 0.2s;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .action-btn:hover:not(:disabled) {
    background: #f1f5f9;
    border-color: var(--c-law-blue);
    color: var(--c-law-blue);
    transform: translateY(-1px);
  }

  .action-btn:active:not(:disabled) {
    transform: translateY(0);
  }

  .action-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  .action-btn i {
    font-size: 14px;
  }

  /* 变量输入面板 */
  .variables-panel {
    border-top: 1px solid #e5e7eb;
    background: #ffffff;
  }

  .panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 24px;
    cursor: pointer;
    transition: background 0.2s;
    border-bottom: 1px solid #e5e7eb;
  }

  .panel-header:hover {
    background: #f9fafb;
  }

  .header-left {
    display: flex;
    align-items: center;
    gap: 12px;
  }

  .header-left i {
    font-size: 18px;
    color: var(--c-law-blue);
  }

  .panel-title {
    font-size: 15px;
    font-weight: 600;
    color: #374151;
  }

  .variable-count {
    font-size: 13px;
    color: #9ca3af;
    background: #f3f4f6;
    padding: 2px 10px;
    border-radius: 12px;
  }

  .panel-content {
    padding: 20px 24px;
    max-height: 200px;
    overflow-y: auto;
    background: #f9fafb;

    &::-webkit-scrollbar {
      width: 6px;
    }

    &::-webkit-scrollbar-track {
      background: transparent;
    }

    &::-webkit-scrollbar-thumb {
      background: #d1d5db;
      border-radius: 3px;
    }

    &::-webkit-scrollbar-thumb:hover {
      background: #9ca3af;
    }
  }

  /* 输入栏内参数弹窗内容 */
  .composer-variables {
    max-height: 360px;
    overflow-y: auto;
    padding: 8px 4px;

    &::-webkit-scrollbar {
      width: 6px;
    }

    &::-webkit-scrollbar-track {
      background: transparent;
    }

    &::-webkit-scrollbar-thumb {
      background: #d1d5db;
      border-radius: 3px;
    }

    &::-webkit-scrollbar-thumb:hover {
      background: #9ca3af;
    }
  }

  /* 输入参数按钮强化样式 */
  .parameters-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 0 10px;
    position: relative;
    color: #409EFF; /* 默认蓝色 */
    transition: color 0.3s ease;
  }

  .parameters-btn .btn-text {
    font-size: 12px;
  }

  .parameters-btn .badge {
    position: absolute;
    top: -6px;
    right: -6px;
    background: #ef4444;
    color: #ffffff;
    border-radius: 999px;
    height: 16px;
    min-width: 16px;
    line-height: 16px;
    text-align: center;
    font-size: 11px;
    padding: 0 4px;
  }

  /* 未填写必填项时显示红色 */
  .parameters-btn.alert {
    color: #ef4444;
  }

  .variable-item {
    margin-bottom: 14px;

    &:last-child {
      margin-bottom: 0;
    }
  }

  .variable-label {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 8px;
    font-size: 14px;
    color: #374151;
    font-weight: 500;
  }

  .required-mark {
    background: #fee2e2;
    color: #dc2626;
    font-size: 12px;
    padding: 2px 8px;
    border-radius: 4px;
  }

  .optional-mark {
    background: #f3f4f6;
    color: #6b7280;
    font-size: 12px;
    padding: 2px 8px;
    border-radius: 4px;
  }

  .file-upload-area,
  .file-list-upload-area {
    background: #ffffff;
    border: 1px dashed #d1d5db;
    border-radius: 8px;
    padding: 16px;
  }

  .upload-placeholder {
    display: flex;
    align-items: center;
    gap: 12px;
  }

  .upload-hint {
    font-size: 12px;
    color: #9ca3af;
  }

  .file-card {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px;
    background: #f9fafb;
    border: 1px solid #e5e7eb;
    border-radius: 8px;

    i.el-icon-document {
      font-size: 24px;
      color: var(--c-law-blue);
    }
  }

  .file-info {
    flex: 1;
    overflow: hidden;
  }

  .file-name {
    font-size: 13px;
    color: #374151;
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .file-size {
    font-size: 12px;
    color: #9ca3af;
    margin-top: 2px;
  }

  .file-list {
    margin-top: 12px;
    display: flex;
    flex-direction: column;
    gap: 8px;
  }

  /* 输入区域样式优化 */
  .input-area {
    padding: 16px 24px 20px;
    border-top: 1px solid rgba(185, 28, 28, 0.18);
    background: transparent;
    backdrop-filter: none;
    position: relative;
    flex-shrink: 0;
  }

  .input-area::before {
    content: "";
    position: absolute;
    top: 0;
    left: 10%;
    width: 80%;
    height: 1px;
    background: linear-gradient(
        to right,
        transparent,
        rgba(248, 113, 113, 0.55),
        transparent
    );
  }

  .input-wrapper {
    width: 100%;
    max-width: 100%;
    pointer-events: auto;
  }

  .composer {
    width: 100%;
    background: #ffffff;
    border: 2px solid #fee2e2;
    border-radius: 16px;
    padding: 12px 16px;
    transition: all 0.3s ease;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
    position: relative;
  }

  .composer:focus-within {
    border-color: #fb7185;
    /* 去掉强烈的外发光，仅保留一圈细边框高亮 */
    box-shadow: 0 0 0 1px rgba(248, 113, 113, 0.35);
    transform: translateY(-1px);
  }

  /* 附件区域样式优化 */
  .attachments {
    margin-bottom: 16px;
    padding-bottom: 16px;
    border-bottom: 1px solid #f1f5f9;
  }

  .attachments-header {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 12px;
    padding: 8px 12px;
    background: #fff7f7;
    border-radius: 8px;
    border-left: 3px solid #fecaca;
  }

  .attachments-header i {
    font-size: 16px;
    color: #fb7185;
  }

  .attachments-count {
    font-size: 14px;
    font-weight: 600;
    color: #374151;
  }

  .attachment-list {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
  }

  .attachment-card {
    display: flex;
    align-items: center;
    gap: 10px;
    background: #f9fafb;
    border: 1px solid #e5e7eb;
    border-radius: 8px;
    padding: 8px 12px;
    position: relative;
  }

  .file-icon {
    width: 24px;
    height: 24px;
    border-radius: 6px;
    background: #fff1f2;
    color: #fb7185;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    flex-shrink: 0;
  }

  .file-meta {
    flex: 1;
    overflow: hidden;
  }

  .file-name {
    font-size: 12px;
    color: #374151;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .file-size {
    font-size: 11px;
    color: #9ca3af;
  }

  .att-remove {
    position: absolute;
    top: -6px;
    right: -6px;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    border: 1px solid #ffffff;
    background: #ef4444;
    color: #ffffff;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    font-size: 12px;
  }

  .att-remove:hover {
    background: #dc2626;
  }

  .input-row {
    display: flex;
    align-items: flex-end;
    gap: 8px;
  }

  .input {
    flex: 1;
    min-height: 24px;
    max-height: 120px;
    border: none;
    outline: none;
    font-size: 14px;
    color: #111827;
    background: transparent;
    resize: none;
    font-family: inherit;
    line-height: 1.5;
  }

  .input::placeholder {
    color: #9ca3af;
  }

  .actions {
    display: flex;
    align-items: center;
    gap: 6px;
  }

  .hidden-file {
    display: none;
  }

  .popover-center-ref {
    position: absolute;
    left: 50%;
    top: 0;
    width: 0;
    height: 0;
  }

  .attach,
  .send,
  .stop {
    width: 36px;
    height: 36px;
    border-radius: 12px;
    border: none;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
    flex-shrink: 0;
  }

  .attach {
    background: transparent;
    color: #9ca3af;
  }

  .attach:hover:not(:disabled) {
    background: rgba(251, 113, 133, 0.12);
    color: #fb7185;
    transform: scale(1.05);
  }

  .send {
    background: linear-gradient(135deg, #fb7185 0%, #f97373 100%);
    color: #ffffff;
    /* 减弱按钮阴影，避免强烈“发光”效果 */
    box-shadow: 0 2px 8px rgba(248, 113, 113, 0.25);
  }

  .send:hover:not(:disabled) {
    transform: scale(1.05);
    box-shadow: 0 3px 10px rgba(248, 113, 113, 0.3);
  }

  .send:active:not(:disabled) {
    transform: scale(0.95);
  }

  .stop {
    background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
    color: #ffffff;
    box-shadow: 0 4px 16px rgba(239, 68, 68, 0.25);
  }

  .stop:hover {
    transform: scale(1.05);
    box-shadow: 0 6px 24px rgba(239, 68, 68, 0.35);
  }

  .stop:active {
    transform: scale(0.95);
  }

  .attach:disabled,
  .send:disabled {
    opacity: 0.4;
    cursor: not-allowed;
    transform: none !important;
  }

  .attach i,
  .send i,
  .stop i {
    font-size: 16px;
  }

  /* 下一步问题建议（消息底部）*/
  .suggested-questions-container {
    margin: 16px auto;
    padding: 14px 18px;
    background: linear-gradient(135deg, #f9fafb 0%, #ffffff 100%);
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    animation: fadeIn 0.3s ease;
    box-shadow: 0 6px 20px rgba(17, 24, 39, 0.06);
    max-width: 900px;
    width: calc(100% - 48px);
    box-sizing: border-box;
  }

  /* 响应式：超大屏幕 */
  @media (min-width: 1400px) {
    .suggested-questions-container {
      max-width: 1000px;
    }
  }

  /* 响应式：中等屏幕 */
  @media (max-width: 768px) {
    .suggested-questions-container {
      width: calc(100% - 32px);
      margin: 16px auto;
    }
  }

  .suggested-questions-title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 12px;
    color: #6b7280;
    font-size: 13px;
    font-weight: 600;
  }

  .suggested-questions-title i {
    font-size: 16px;
    color: var(--c-law-blue);
  }

  .suggested-questions-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
  }

  .suggested-question-chip {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 16px;
    background: #ffffff;
    border: 1px solid #e5e7eb;
    border-radius: 8px;
    cursor: pointer;
    transition: all 0.2s;
  }

  .suggested-question-chip:hover {
    background: #f1f5f9;
    border-color: var(--c-law-blue);
    transform: translateX(4px);
  }

  .suggested-question-chip span {
    flex: 1;
    font-size: 14px;
    color: #374151;
    line-height: 1.5;
  }

  .suggested-question-chip i {
    font-size: 14px;
    color: #9ca3af;
    transition: color 0.2s;
  }

  .suggested-question-chip:hover i {
    color: var(--c-law-blue);
  }

  /* 文件链接 */
  .message-text ::v-deep .file-link-wrapper {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    background: #f3f6ff;
    border: 1px solid #e0e7ff;
    border-radius: 6px;
    margin: 4px 0;
    transition: all 0.2s;
  }

  .message-text ::v-deep .file-link-wrapper:hover {
    background: #eef2ff;
    border-color: var(--c-primary);
    transform: translateX(2px);
  }

  .message-text ::v-deep .file-link-wrapper i {
    font-size: 18px;
    color: var(--c-law-blue);
  }

  .message-text ::v-deep .file-link {
    color: #374151;
    text-decoration: none;
    font-size: 14px;
    font-weight: 500;
    transition: color 0.2s;
  }

  .message-text ::v-deep .file-link:hover {
    color: var(--c-law-blue);
    text-decoration: underline;
  }

  /* ========== Chrome 75-77 兼容：无 flex-gap 时的间距补偿 ========== */
  @supports not (gap: 1rem) {
    /* 欢迎区标签组 */
    .tag-group > * {
      margin-right: 12px;
      margin-bottom: 8px;
    }

    .opening-questions-list > * {
      margin-right: 10px;
      margin-bottom: 8px;
    }

    .cap-tag > * + * {
      margin-left: 6px;
    }

    /* 主功能卡片区域 */
    .sub-card > * + * {
      margin-left: 16px;
    }

    .kb-section > * + * {
      margin-top: 16px;
    }

    .kb-header > * + * {
      margin-left: 8px;
    }

    .kb-tags-container > * {
      margin-right: 12px;
      margin-bottom: 8px;
    }

    .kb-tag > * + * {
      margin-left: 8px;
    }

    /* 欢迎提示与变量提示 */
    .welcome-bubble-tip > * + * {
      margin-left: 8px;
    }

    .welcome-variable-tip > * + * {
      margin-left: 8px;
    }

    /* 消息操作区域 */
    .message-actions > * + * {
      margin-left: 6px;
    }

    .action-buttons > * + * {
      margin-left: 2px;
    }

    /* 变量面板头部 */
    .header-left > * + * {
      margin-left: 12px;
    }

    .parameters-btn > * + * {
      margin-left: 6px;
    }

    .variable-label > * + * {
      margin-left: 6px;
    }

    /* 上传区域与文件卡片 */
    .upload-placeholder > * + * {
      margin-left: 12px;
    }

    .file-card > * + * {
      margin-left: 12px;
    }

    .attachments-header > * + * {
      margin-left: 8px;
    }

    .attachment-list > * + * {
      margin-left: 10px;
    }

    .attachment-card > * + * {
      margin-left: 10px;
    }

    .input-row > * + * {
      margin-left: 8px;
    }

    /* 推荐问题与建议问题 */
    .suggested-questions-title > * + * {
      margin-left: 8px;
    }

    .suggested-questions-list > * + * {
      margin-top: 8px;
    }

    /* 消息中文件链接 */
    .message-text ::v-deep .file-link-wrapper > * + * {
      margin-left: 8px;
    }

    /* 用户消息与内联文件 */
    .user-message-text > * + * {
      margin-top: 8px;
    }

    .user-files-inline > * + * {
      margin-top: 6px;
    }

    .user-file-inline-item > * + * {
      margin-left: 8px;
    }

    /* 引用信息芯片 */
    .message-text ::v-deep .references-inline-header > * + * {
      margin-left: 6px;
    }

    .message-text ::v-deep .references-chip-list > * {
      margin-right: 8px;
      margin-bottom: 8px;
    }

    .message-text ::v-deep .reference-chip > * + * {
      margin-left: 6px;
    }

    /* 打字中指示器 */
    .typing-indicator > * + * {
      margin-left: 6px;
    }
  }

  @supports ((-webkit-backdrop-filter: blur(8px)) or (backdrop-filter: blur(8px))) {
    .sidebar {
      background: rgba(255, 255, 255, 0.6);
      -webkit-backdrop-filter: blur(12px);
      backdrop-filter: blur(12px);
    }

    .composer {
      background: rgba(255, 255, 255, 0.72);
      -webkit-backdrop-filter: blur(10px);
      backdrop-filter: blur(10px);
    }

    .message-wrapper.assistant .message-text {
      background: rgba(255, 255, 255, 0.9);
      -webkit-backdrop-filter: blur(6px);
      backdrop-filter: blur(6px);
    }
  }

  /* 用户消息内容容器 */
  .user-message-content {
    width: 100%;
  }

  .user-message-text {
    display: flex;
    flex-direction: column;
    gap: 8px;
  }

  .user-message-text-body {
    width: 100%;
    color: inherit;
  }

  /* 用户消息框体内的文件显示 */
  .user-files-inline {
    display: flex;
    flex-direction: column;
    gap: 6px;
  }

  .user-file-inline-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    width: 100%;
    background: rgba(255, 255, 255, 0.95);
    border: 1px solid rgba(255, 255, 255, 0.9);
    border-radius: 6px;
    transition: all 0.2s;
    cursor: pointer;
  }

  .user-file-inline-item:hover {
    background: #eff6ff;
    border-color: #bfdbfe;
    transform: translateX(-2px);
  }

  .user-file-inline-item i {
    font-size: 16px;
    color: #3b82f6;
    flex-shrink: 0;
  }

  .user-file-inline-name {
    flex: 1;
    font-size: 13px;
    color: #1f2937;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* 引用和归属 - 文件名芯片式（点击弹窗），配色贴合征信页面主题 */
  /* 引用和归属 - 重构为卡片 + 竖直文件列表，避免文字挤出框体 */
  .message-text ::v-deep .references-section {
    margin-top: 14px;         /* 与正文拉开一点距离 */
    margin-bottom: 4px;
    padding: 12px 14px;       /* 增大内边距，避免内容贴边 */
    border-radius: 10px;
    background: #f9fafb;
    border: 1px solid #e2e8f0;
    box-shadow: 0 2px 8px rgba(15, 23, 42, 0.04);
    max-width: 100%;
    box-sizing: border-box;
  }

  .message-text ::v-deep .references-inline-header {
    display: flex;
    align-items: center;
    gap: 6px;
    color: #6b7280;
    font-weight: 600;
    margin-bottom: 6px;
    font-size: 12px;
  }

  .message-text ::v-deep .references-inline-header i {
    font-size: 14px;
  }

  .message-text ::v-deep .references-inline-label {
    letter-spacing: 2px;
  }

  /* 竖直列表，每个引用占一行，宽度跟随父容器，不会溢出 */
  .message-text ::v-deep .references-chip-list {
    display: flex;
    flex-direction: column;
  }

  .message-text ::v-deep .reference-chip {
    display: flex;
    align-items: center;
    gap: 10px;                /* 图标与文字的间隙更大一点，防止旧版浏览器重叠 */
    width: 100%;
    padding: 8px 12px;        /* 行内上下左右 padding 增大 */
    border-radius: 6px;
    border: 1px solid #e5e7eb;
    background: #ffffff;
    color: #111827;
    cursor: pointer;
    transition: background 0.16s ease, border-color 0.16s ease, transform 0.16s ease;
    font-size: 13px;
    line-height: 1.5;         /* 提升可读性，避免行高过小导致内容挤在一起 */
    box-sizing: border-box;
    margin-bottom: 8px;       /* 使用 margin 兼容旧版 Chrome（不支持 flex gap/row-gap） */
  }

  .message-text ::v-deep .reference-chip i {
    font-size: 14px;
    color: #2563eb;
    flex-shrink: 0;           /* 图标宽度固定，避免和文字互相压缩 */
  }

  .message-text ::v-deep .reference-chip:hover {
    background: #eff6ff;
    border-color: #bfdbfe;
    transform: translateY(-1px);
  }

  .message-text ::v-deep .reference-chip .chip-name {
    flex: 1 1 auto;
    min-width: 0;
    white-space: normal;
    word-break: break-all;
  }

  .message-text ::v-deep .reference-chip .chip-score {
    margin-left: 4px;
    font-size: 11px;
    color: #6b7280;
    background: #f3f4f6;
    padding: 2px 6px;
    border-radius: 999px;
  }

  .message-text ::v-deep .reference-chip.view-all {
    background: #f9fafb;
    border-style: dashed;
  }

  /* 加载动画 */
  .typing-indicator {
    display: flex;
    gap: 6px;
    padding: 16px 20px;
    background: #ffffff;
    border: 1px solid #e5e7eb;
    border-radius: 16px;
    border-bottom-left-radius: 4px;
    width: fit-content;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
  }

  .typing-indicator span {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--c-law-blue) 0%, var(--c-law-dark) 100%);
    animation: typing 1.4s infinite ease-in-out;
  }

  .typing-indicator span:nth-child(2) {
    animation-delay: 0.2s;
  }

  .typing-indicator span:nth-child(3) {
    animation-delay: 0.4s;
  }

  @keyframes typing {
    0%, 60%, 100% {
      opacity: 0.3;
      transform: scale(0.7);
    }
    30% {
      opacity: 1;
      transform: scale(1.1);
    }
  }
  </style>
